您的位置:首页 > 运维架构 > Linux

阻塞信号和捕捉信号

2016-07-27 00:10 369 查看
一 阻塞信号

1 概念:

信号递达:实际执行信号的处理动作称为信号递达(Delivery)。
信号未决:信号从产生到递达之间的状态,称为信号未决(Pending)。
进程可以选择阻塞(Block )某个信号。被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。
注意:阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。
每个信号都有两个标志位分别表示阻塞(block)和未决(pending),还有一个函数指针表示处理动作。信号产生时,内核在进程控制块中设置该信号的未决标志,直到信号递达才清除该标志。
未决和阻塞标志可以用相同的数据类型sigset_t来存储,sigset_t称为信号集,这个类型可以表示每个信号的“有效”或“无效”状态,在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有效”和“无效”的含义是该信号是否处于未决状态。

2 信号集操作函数:

#include <signal.h>
int sigemptyset(sigset_t *set);//初始化set指向的信号集,使所有bit位全为0
int sigfillset(sigset_t *set);//初始化所有bit位全为1
int sigaddset(sigset_t *set, int signo);//添加某种有效信号
int sigdelset(sigset_t *set, int signo);//删除某种有效信号
int sigismember(const sigset_t *set, int signo);//判断某种信号的有效性,有效为1,无效为0


3 信号屏蔽字:

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);//读取或更改信号屏蔽字(阻塞信号集)


4 未决信号集:

#include <signal.h>
int sigpending(sigset_t *set);//读取当前进程的信号未决集,调用成功返回1,失败返回-1


5 进程阻塞验证小程序:







运行程序可以看出:程序每隔一秒打印一次当前进程的未决信号集,因为信号SIGINT被阻塞,当我们按下Ctrl-c时,信号SIGINT会处于未决状态,所以进程不能终止。因为信号SIGQUIT没有被阻塞,所以当我们按下Ctrl-\时,进程会退出。(通过ps
aux|grep 进程名,来找到该进程)
二 捕捉信号

1 概念:

捕捉信号:如果信号的处理动作是用户自定义函数(信号处理代码在用户空间),在信号递达时就调用这个函数,这称为捕捉信号。

2 sigaction函数:

#include <signal.h>
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);//修改进程对信号的默认处理方式
解释:sigaction函数可以读取和修改与指定信号相关联的处理动作。调用成功则返回0,出错则返回- 1。signo是指定信号的编号。若act指针非空,则根据act修改该信号的处理动作。若oact指针非空,则通过oact传出该信号原来的处理动作。act和oact指向sigaction结构体:
struct sigaction
{
void (*sa_handler)(int);
sigset_t sa_mask;
int flags;
void (*sa_sigaction)(int, siginfo_t*, void*);
};

对于自定义函数捕捉信号(向内核注册一个信号处理函数)有以下要求:

该函数返回值为void,可以带一个int参数,通过参数可以得知当前信号的编号,这样就可以用同一个函数处理多种信号。显然,这也是一个回调函数,不是被main函数调用,而是被系统所调用。

注意:当某个信号的处理函数被调用时,,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么 它会被阻塞到当前处理结束为止。

3 pause函数:

#include <unistd.h>
int pause(void);//使调用的进程挂起直到信号递达4000
>解释:如果信号的处理动作是终止进程,则进程终止,pause函数没有机会返回;如果信号的处理动作是忽略,则进程继续处于挂起状态,pause不返回;如果信号的处理动作是捕捉,则调用了信号处理函数之后pause返回-1,errno设置为EINTR, 所以pause只有出错的返回值(想想以前还学过什么函数只有出错返回值?exec())。错误码EINTR表示“被信号中断”。

4 my_sleep()小程序







1. main函数调用my_sleep函数,后者调用sigaction注册了SIGALRM信号的处理函数catch。

2. 调用alarm(timeout)设定闹钟。

3. 调用pause等待,内核切换到别的进程运行。

4. timeout秒之后,闹钟超时,内核发SIGALRM给这个进程。

5. 从内核态返回这个进程的用户态之前处理未决信号,发现有SIGALRM信号,其处理函数是catch。

6. 切换到用户态执行catch函数,进⼊入catch函数时SIGALRM信号被自动屏蔽, 从catch函数返回时SIGALRM信号自动解除屏蔽。然后自动执行系统调用sigreturn再次进入内核,再返回用户态继续执行进程的主控制流程(main函数调用的my_sleep函数)。

7. pause函数返回-1,然后调用alarm(0)取消闹钟,调用sigaction恢复SIGALRM信号以前的处理动作。

注意:

注册catch函数的原因:

pause函数只有遇到用户自定义的信号捕捉函数时才会返回。所以为了让pause函数返回才定义的catch()函数。

为什么在my_sleep函数返回前要恢复SIGALRM信号原来的sigaction?

不恢复也可以,但是我们要遵守一定的规则,即不要改变函数原先的行为,所以就要恢复它本来的行为。这样也方便我们再次调用时把握它的处理方式。

my_sleep函数的返回值表示什么含义?什么情况下返回非0值?

返回0表示取消闹钟,已经唤醒成功。如果在闹钟还没响时就取消闹钟,返回值表示还有多久闹钟才会响。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux 信号 阻塞 捕捉