1、信号的其他的常见的概念
- 实际执行信号的处理动作称为信号抵达(Delivery)
- 信号从产生到抵达之间的状态称为信号未决(Pending)
- 进程可以选择阻塞(Block)某个信号
- 被阻塞的信号产生时保持在未决状态,直到进程解除对此信号的阻塞,才执行抵达的动作
- 注意阻塞和忽略是不同的,只要信号被阻塞就不会抵达,而忽略是在抵达之后可选的一种处理动作。
2、内核中的表示
我们可以从中知道:
1)每个信号都有两个标志位分别表示堵塞(block)和未决(pending)还有一个函数指针来表示处理动作。
2)信号产生时,内核在进程控制块中设置该信号的未决标志,直到信号抵达才清除该标志。
3)在上面的这个例子中我们可以看到的是SIGHUP这个信号未阻塞也从未产生过,,当它抵达的时候执行默认处理动作。SIGINT信号产生过,但正在被阻塞,所以并不能抵达,虽然它的处理动作是忽略,但是在没有解除堵塞之前,不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除堵塞。
4)SIGQUIT信号从未产生过,一旦产生将被堵塞,它的处理动作是用户自定义函数sighandler。
5)Linux中,常规信号,在抵达之前产生多次,只计算一次,而实时信号在抵达之前产生多次可以依次的放在队列里。
3、信号集(sigset_t)
从上图看出,每个信号只有一个bit的未决标志,非0即1,不记录该信号产生了多少次,阻塞标志也是这样表示的,因此阻塞和未决可以用相同的数据类型sigset_t来存储,sigset_t被称为信号集。
阻塞信号集也叫做当前进程的信号屏蔽字(Signal Mask)这里的屏蔽理解成阻塞而不是“忽略”。
4、信号集操作函数
sigset_t类型对于每种信号用一个bit表示“有效”和“无效”两种状态。使用者只能调用下面的函数来操作信号集变量,而不是直接对它的内部数据做任何解释,比如用printf直接打印sigset_t变量是没有意义的。
#include <signal.h>
//初始化set指向的信号集,使其中所有信号对应的bit位都清零,表示该信号集不包括任何有效信号,成功返回0,出错返回-1
int sigemptyset(sigset_t *set);
//初始化set指向的信号集,使其中所有的信号对应的bit位置为1,表示该信号集的有效信号,包括系统支持的所有信号,成功返回0,出错返回-1
int sigfillset(sigset_t *set);
//添加某个有效信号,成功返回0,出错返回-1
int sigaddset(aigset_t *set,int signo);
//删除某个有效信号,成功返回0,失败返回-1
int sigdeset(sigset_t *set,int signo);
//判断一个信号集的有效信号中是否包含某个信号,若包含返回1,不包含返回0,若出错返回-1
int sigismember(const sigset_t *set,int signo);
sigprocmask函数
调用该函数sigprocmask可以读取或更改进程的信号屏蔽字(阻塞信号集)
假设当前的信号屏蔽字为mask,下表说明了how参数的可选值
SIG_BLOCK | set包含了我们希望添加到当前信号屏蔽字的信号,相当于mask=mask~set |
---|---|
SIG_UNBLOCK | set包含了我们希望从当前信号屏蔽字中解除阻塞的信号,相当于mask=mask&~set |
SIG_SETMASK | 设置当前信号屏蔽字为set所指向的值,相当于mask=set |
如果调用了sigprocmask函数解除了对当前若干个未决信号的阻塞,则在sigprocmask返回前,至少将其中的一个信号抵达。
sigpending函数
读取当前进程的信号未决信号集,通过set参数传出。调用成功返回0,出错返回-1
简单的小应用:
1 #include <stdio.h>
2 #include <signal.h>
3 #include <unistd.h>
4
5 void printsigset(sigset_t *set)
6 {
7 int i=0;
8 for(;i<32;i++)
9 //以此判断第i个信号是否为有效信号
10 {
11 if(sigismember(set,i))
12 {
13 //如果试有效信号则输出
14 putchar('1');
15 }
16 else
17 //不是有效信号输出0
18 putchar('0');
19 }
20 puts(" ");
21 }
22
23 int main()
24{
25 //定义两个信号集
26 sigset_t s,p;
27 // 将信号集s初始化,即使其中所有信号对应的bit位置为0,表示该信号集不包括任何有效的信号
28 sigemptyset(&s);
29 //将SIGINT信号在信号集s中设置为有效信号
30 sigaddset(&s,SIGINT);
31 //调用sigprocmask函数,更改信号屏蔽字,将信号集s中的有效信号添加到当前进程的阻塞信号集
32 //即将SIGINT信号阻塞
33 sigprocmask(SIG_BLOCK,&s,NULL);
34 while(1)
35 {
36 //调用该函数读取当前进程的未决信号集
37 sigpending(&p);
38 //每隔1秒打印当前进程的未决信号集
39 printsigset(&p);
40 sleep(1);
41 }
42 return 0;
43 }
44
程序运行时,每秒钟把各信号的未决状态打印一遍,由于我们阻塞了SIGINT信号,按Ctrl+C将会一直使SIGINT信号处于未决状态,按Ctrl +\仍然可以终止程序,因为SIGQUIT信号没有阻塞。