一. 阻塞信号
1.信号的相关概念
(1) 递达: 实际执行信号的处理动作称为信号的递达
(2) 未决: 信号从产生到递达之间的过程叫做信号的未决
(3) 阻塞: 进程可以选择阻塞某个信号, 被阻塞的信号产生时将保持在未决状态, 直到进程解除该信号的屏蔽, 才执行递达动作.
(4) 阻塞和忽略的不同: 信号被阻塞就不会被递达, 而信号被忽略是该信号递达的一种方式
(5) 信号递达的几种方式: 忽略, 默认处理, 自定义
二. 信号在内核中的表示
每个信号都有两个标志位表示该信号的阻塞和未决还有一个函数指针表示该信号的处理动作, 信号产生时,内核在进程控制块中设置该信号的未决标识, 直到该信号递达才清除该标志. 在图上 SIGUP 信号未产生过, 也为阻塞过, 当它递达时执行默认动作, SIGINT 信号产生过, 并且处于阻塞状态, 该信号暂时不能被递达, 直到被解除阻塞. SIGQUIT 未产生, 一旦产生, 该信号就被阻塞, 它的处理动作是自定义. 如果在进程解除对某信号的阻塞前, 信号产生过多次, 该信号该怎样计算呢? POSIX.1 允许信号被递达一次或者多次, Linux 中规定对于普通信号, 在递达之前产生多次知己一次, 而对于实时信号递达多次可以放在一个队列里.
三. sigset_t
由图中可以看出, 每个信号只有一个 bit 位表示未决, 一个bit 位表示阻塞, 于是可以用 sigset_t 的数据类型来对两者进行存储. sigset_t 它是一个信号集, 用来表示该信号的有效或者无效. 在阻塞表中表示是否阻塞, 在未决表中表示该信号是否未决.阻塞信号集也叫做当前信号的屏蔽字
四. 相关接口函数
1.信号集相关接口
(1)用于初始化set 所指向的所有信号集, 将 set 所指向的信号集中的所有信号的 bit 全部清零, 表示该信号集不含任何有效信号.
(2)sigfillset 用于初始化set信号集所指向的所有信号, 将其中所有信号的 bit 位置位, 该信号集的有效信号包括该系统中支持的所有信号
(3)使用 sigset_t 类型的 变量的时候一定要用 sigsetempty 以及 sigfillset 初始化, 使得信号集处于确定状态, 初始化之后就可以调用 sigaddset 和 sigdelset 在该信号集中添加或者删除 某个信号
(4) sigismember 用来判断某个信号集中是否包含某个信号, 如果有返回 1, 没有返回 0, 调用失败返回 -1.
(5)不能对 sigset_t 类型的变量做任何解释, 如打印等操作, 因为它是系统实现的使用者不必关心
2. 阻塞信号集相关接口
(1)用来读取或者更改信号屏蔽字(阻塞的信号集), 成功时返回0, 出错时返回 -1.
(2)how 用来表示更改屏蔽字的方式
(3)oldset 表示原有的阻塞信号集(信号屏蔽字), set 表示需要修改的信号屏蔽字(阻塞信号集).
how 有三个取值, 假设当前的屏蔽字是mask 则SIG_BLOCK 相当于 mask = mask | set, SIG_UNBLOCK 相当于 mask = mask &~set, SIG_SETMASK 相当于 mask = set
3. 未决信号集相关接口
用于读取当前进程的未决信号集, set 是一个输出型参数, 有用户自己设定. 调用成功返回0, 调用失败返回 -1.
4. 代码演示
#include<stdio.h>
#include<signal.h>
#include<sys/types.h>
#include<unistd.h>void sigprint(sigset_t* set)
{if(set == NULL){return;}int i = 0;for(; i < 32; i++){if(sigismember(set, i)){putchar('1');}else{putchar('0');}}printf("\n");
}int main()
{sigset_t set, p;sigset_t oset;sigemptyset(&p);sigemptyset(&set);sigaddset(&set, SIGINT);sigprocmask(SIG_BLOCK, &set, &oset);int count = 10;while(count){sigpending(&p);sigprint(&p);sleep(1);count--;}sigprocmask(SIG_BLOCK, &oset, NULL);return 0;
}
此时发现当按 Ctrl + C 的时候进程不能停止, 因为Ctrl + C 是2 号信号, 该信号被阻塞, 当产生一个二号信号时, 操作系统将其放到未决状态, 不能被递达, 因此 Ctrl + C 已经能再终止进程, 当按一下 Ctrl + \ (3)号信号,可以终止进程, 因为该进程没有被阻塞.