内容: 记录linux阻塞信号的知识
信号在内核的状态:
1.信号递达:实际执行信号的处理动作称为信号递达;
2.信号未决:信号从产生到递达之间的状态;
3.信号阻塞:被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作;
内核进程控制块中函数有信号屏蔽状态字(block)和信号未决状态字(pending)——64bit,一个bit位代表一个信号:
信号屏蔽状态字(block),1代表阻塞、0代表不阻塞;
信号未决状态字(pending)的1代表未决,0代表信号可以抵达了;
阻塞过程:
当向进程发送信号时,内核首先判断信号屏蔽状态字是否阻塞,如果该信号被设为为了阻塞的,那么信号未
决状态字(pending)相应位制成1;若该信号阻塞解除,信号未决状态字(pending)相应位制成0;表示
信号此时可以抵达了,也就是可以接收该信号了。
注意:屏蔽状态字用户可以读写,未决状态字用户只能读
每个信号都有两个标志位分别表示阻塞(block)和未决(pending),还有一个函数指针用来表示处理动作。信号产生时,内核在PCB中设置该信号的未决状态,直到信号递达才清除该标志。
信号处理规则如下:
1.信号未阻塞也未产生过(未决),当它递达时执行信号回调函数
2.信号产生了,但是正在被阻塞(block),所以暂时不能被递达。但是在没有解除阻塞之前不能忽略这个
信号,哪怕信号处理方式是忽略。因为进程仍有机会改变处理动作之后再解除阻塞。
3.信号未产生过,而且一旦产生就阻塞,它的处理动作是用户自定义的信号处理函数sighandler。
(注意:常规信号在递达之前产生多次只记一次,而实时信号在递达之前产生多次可依次放在一个队列里面)
在阻塞信号集中:代表是否阻塞;在未决信号集中:代表信号是否处于未决状态。
信号集处理函数:
#include <signal.h>
int sigemptyset(sigset_t *set);//将信号集清空,共64bits
int sigfillset(sigset_t *set);//将信号集置1
int sigaddset(sigset_t *set, int signum);//将signum对应的位置为1
int sigdelset(sigset_t *set, int signum);//将signum对应的位置为0
int sigismember(const sigset_t *set, int signum);//判断signum是否在该信号集合中
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
第一个参数代表操作内容:
SIG_BLOCK: 将参数set指向的信号集中设置的信号添加到现在的屏蔽状态字中,设置为阻塞;
SIG_UNBLOCK:将参数set指向的信号集中设置的信号添加到现在的屏蔽状态字中,设置为非阻塞
SIG_SETMASK:将参数set指向的信号集直接覆盖现在的屏蔽状态字的值;
第二个参数代表操作的信号集
第三个参数用于获取原来信号的处理方式,是个出参
函数返回值:若成功则为0,若出错则为-1
int sigpending(sigset_t *set);读取未决信号集状态
设置信号为阻塞:
sigset_t block_set;
sigemptyset(&block_set);
sigaddset(&block_set, 具体信号);
sigprocmask(SIG_BLOCK, &block_set, NULL);
解除信号阻塞:
sigset_t unblock_set;
sigemptyset(&unblock_set);
sigaddset(&unblock_set, SIGINT);
sigprocmask(SIG_UNBLOCK, &unblock_set, NULL);
注册信号处理函数:
signal(具体信号, handler);
打印未决状态字:
sigset_t test_set;
sigpending(&test_set);
for (i = 1; i <= 64; ++i) {
if(sigismember(test_set, i))
cout << 1;
else
cout << 0;
}
大四学生一枚,文章均非抄袭或者模仿,均为原创,仅代表个人观点,如果文章有错误的地方,欢迎在下方提出,每条评论我都会去认真看并回复,同时感谢指正的前辈