1,信号集
我们可以将多个信号编成一个集合,对集合内的信号进行同样的操作(比如忽略一个信号集内的所有信号)。信号集的数据类型为sigset_t。还有一些与之相关的函数用于初始化信号集、向信号集中添加、删除信号,以及判断某个信号是否在信号集内。
原型如下:
#include<signal.h>
int sigemptyset(sigset_t *set); //将set指向的信号集初始化为不包含任何信号
int sigfillset(sigset_t *set); //将set指向的信号集初始化为包含所有信号
int sigaddset(sigset_t *set, int signo);//向信号集添加信号signo
int sigdelset(sigset_t *set, int signo);//向信号集删除信号signo
int sigismember(const sigset_t *set, int signo);//判断信号signo是否在信号集中
信号集在使用前一定要用sigemptyset或sigfillset初始化,不能指望于C语言的默认初始化(可惜了,没有C++里的默认构造函数)。
2,sigprocmask
现在我们可以尝试屏蔽掉一个信号集里的所有信号了。
一个进程是否屏蔽某个信号是由该进程的信号屏蔽字决定的。信号屏蔽字是一个信号集,其包含的信号都会被阻塞而不能递送给这个进程。而sigprocmask就是用来修改进程的信号屏蔽字的,因此它可以屏蔽某个信号会对某个已经屏蔽的信号解除屏蔽。
其原型如下:
int sigprocmask(int how, const sigset_t *restrict set, const sigset * restrict oset);
其中,
restrict是C99的关键字,代表这两个指针不会指向同一个对象;
oset若不为空,则会获得修改前的信号屏蔽字;
set是将会对当前信号屏蔽字产生影响的信号集。可以用set对当前信号屏蔽字进行或运算、且运算或复制运算。具体是哪种运算,就由how决定。
具体如下:
how == SIG_BLOCK,或运算,与当前屏蔽字取并集,set中指定的信号都会被屏蔽;
how==SIG_UNBLOCK,取反再且运算,set的补集与当前屏蔽字取交集,set中指定的信号都会被解除屏蔽;
how==SETMASK,赋值运算,新的信号屏蔽字被设置为set。
如果set为空呢?这时不会对信号屏蔽字产生任何影响,how的值也就无所谓。
通常,想获取当前信号屏蔽字的时候,就将set设为空,然后通过oset来获取。
sigprocmask(0, NULL, &oset);
这样调用即可。
3,例子
我们设计一段代码,她让当前进程屏蔽SIG_USR1,而不屏蔽SIG_USR2.
然后,我们用kill命令分别向这个进程发送这两个信号,观察现象。
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <setjmp.h>
void sig_usr_new(int signo);
int main(){
//1,注册信号
if(signal(SIGUSR1, sig_usr_new) == SIG_ERR)
{
printf("can't catch SIGUSR1 !\n");
exit(1);
}
if(signal(SIGUSR2, sig_usr_new) == SIG_ERR)
{
printf("can't catch SIGUSR2 !\n");
exit(1);
}
//2, 修改信号屏蔽字
sigset_t sigset;
//2.1 初始化sigset
if (sigemptyset(&sigset) < 0)
{
printf("sigemptyset error !\n");
exit(1);
}
//2.2 添加要屏蔽的信号
if (sigaddset(&sigset, SIGUSR1) < 0)
{
printf("add SIGUSR1 error!\n");
exit(1);
}
//2.3 修改当前信号屏蔽字
if (sigprocmask(SIG_BLOCK, &sigset, NULL) < 0)
{
printf("can't block SIGUSR1!\n");
exit(1);
}
//3,死循环,为了防止进程退出
while(1){
pause();
}
}
void sig_usr_new(int signo)
{
printf("recived SIGUSR, signo = %d\n",signo);
}
我们给当前进程注册了两个信号SIG_USR1和SIG_USR2,这两个信号还绑定到同一个信号处理函数。所以如果它们被触发,会调用同样的函数。然后,我们屏蔽了其中一个信号,观察效果。
➜ code ./study_Linux&
[1] 83
zsh: nice(5) failed: operation not permitted
➜ code kill -USR1 83 //发送SIGUSR1,没有任何函数被调用
➜ code kill -USR2 83 //发送SIGUSR1,有函数被调用
recived SIGUSR, signo = 12
➜ code kill 83
[1] + 83 terminated ./study_Linux
➜ code ps
PID TTY TIME CMD
4 tty1 00:00:01 zsh
105 tty1 00:00:00 ps
注意,有两个信号是无法被屏蔽的,它们死SIGKILL 和SIGSTOP。
如果连这两个信号都能屏蔽,那进程岂不是有可能成为一个怎么都停不下来的流氓进程?!