4 信号阻塞
在进程PCB中存在两个信号集:信号掩码(signal mask)、未决信号集(signal pending)。
两个信号集都是位图,每一位对应一个信号:
- 若mask中某个位被设置为1,则对应的信号将被屏蔽;
- 此时内核会修改pending中该信号对应的位为1,使该信号处于未决态;
- 除非该信号被解除屏蔽,否则内核不会再向进程发送这个信号;
- 用户不能直接操作未决信号集,但可以自定义的set位图与mask进行位操作,以达到屏蔽或解除屏蔽的目的。
4.1 信号集设定函数
#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signum);
int sigdelset(sigset_t *set, int signum);
int sigismember(const sigset_t *set, int signum);
函数功能:设定自定义信号集:
- sigemptyset:将指定信号集清0;
- sigfillset:将指定信号集置1;
- sigaddset:将某个信号加入指定信号集;
- sigdelset:将某个信号从信号集中删除;
- sigismember:判断某个信号是否已加入指定信号集。
参数说明:
- set:一个sigset_t类型的指针,sigset_t是系统自定义类型,其实质是一个位图,一般用户不会对位进行直接操作的,通过上述函数进行间接操作。
返回值说明:
- 成功返回0
- 不成功返回-1
4.2 sigprocmask函数
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
函数功能:自定义位图设定完成后,要与mask位图进行位操作,以改变mask位图中的数据。
参数说明:
- how:用于设置位操作的方式,取值如下:
– SIG_BLOCK:set位图中需要屏蔽的信号,即mask = mask | set;
– SIG_UNBLOCK:set位图中需要解除屏蔽的信号,即mask = mask & ~set;
– SIG_SETMASK:set位图用于替代mask的新屏蔽集,即mask = set。 - set和oldset:指向位图的指针,set指向用户自定义位图,oldset记录原mask位图的值。
返回值说明:
- 成功返回0;
- 不成功返回-1,并设置errno。
特别说明:
系统中什么时候产生什么信号是有规律的,用户进程不应随便对mask进行修改。因此在用户进程中的功能实现之后,应尽量使用sigprocmask的传出参数oldset恢复mask。
4.3 sigpending函数
#include <signal.h>
int sigpending(sigset_t *set);
函数功能:获取当前进程中未决信号集的信息。
参数说明:
- set:一个传出参数,获取未决信号集信息。
返回值说明:
- 1:成功
- -1:不成功
【案例 1】以2号信号为例,通过位操作函数sigprocmask和sigpending获取信号状态。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>void printset(sigset_t *paraPed){int i;for(i = 1; i < 32; i ++){if(sigismember(paraPed, i) == 1){putchar('1');}else{putchar('0');}//of ifprintf("\n");}//of for i
}//of printsetint main(){sigset_t tempSet, tempOldSet, tempPed;sigemptyset(&tempSet); //初始化sigaddset(&tempSet, SIGINT); //将2号信号SIGINT加入tempSetsigprocmask(SIG_BLOCK, &tempSet, &tempOldSet); //位操作while(1){sigpending(&tempPed);printset(&tempPed);sleep(1);}//of whilereturn 0;
}//of main
执行程序,终端会不断打印进程PCB中的未决信号集。初始情况下进程未决信号集中的每一位都应为0,因此打印的信息如下:
00000000000000000000000000000000
使用kill命令或组合按键Ctrl+C驱使内核发送信号SIGINT给当前进程。进程第一次接收到信号SIGINT后,sigprocmask被触发,此后终端打印的信息如下:
01000000000000000000000000000000
之后继续向进程发送SIGINT信号,终端打印信息不变,说明信号SIGINT被成功屏蔽。