一、关于信号的介绍
参见信号的的介绍
二、信号的屏蔽和捕捉忽略
除SIGKILL和SIGDTOP信号外。其他的信号都可以被屏蔽或者忽略。
- 信号忽略:系统仍然传递该信号,只是相应的进程对该信号不做任何处理。
- 信号的屏蔽:该进程对该信号不捕获,而是让该信号
处于未决状态
。只有当进程的信号集发生改变后,不再屏蔽该信号,才捕获信号。
信号的屏蔽只是待信号发生后,让信号处于未决状态,将信号处理进行延后(延至解除屏蔽)再处理,信号的忽略表示自己捕获到该信号,在信号处理函数中什么为也不干直接忽略
1、信号的捕获
参见信号的捕获处理
2、信号的屏蔽
①、信号集
信号集,顾名思义所有信号的集合。在Linux下信号的集合是sigset_t
类型的变量,该类型的定义为unsigned long int
的,该类型在GCC(POSIX系统以及Cygwin)下为8字节。而Linux下一共64个信号,这代表着每个信号占一位(1bit)。当该信号集的第一个bit为1时,表示1号信号处于未决态(被屏蔽准确不应该这么叫),为0时表示1号信号未处于未决态(未被屏蔽)。
信号集的本质是位图,我们在操作信号集的时候不应该直接使用位操作,而是用响应的API操作,保证程序跨平台的有效。
②、信号集的相关操作
(1)将某个信号集清0
int sigemptyset(sigset_t *set);
成功:0
失败:-1
(2)将某个信号集置1
int sigfillset(sigset_t *set);
成功:0
失败:-1
(3)将某个信号加入到信号集
int sigaddset(sigset_t *set, int signum);
成功:0
失败:-1
(4)将某个信号从信号集清除
int sigdelset(sigset_t *set, int signum);
成功:0
失败:-1
(5)判断某个信号是否在信号集中
int sigismember(const sigset_t *set, int signum);
在:1 不在: 0出错:-1
(6)设置进程屏蔽信号集
用来屏蔽/解除屏蔽位于进程PCB中的信号集合
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
假设当前的信号集合为currmask
how参数的取值:
SIG_BLOCK:表示将set信号集合添加到当前进程屏蔽的信号集中,相当于currmask=currmask|set
SIG_UNBLOCK:表示将set信号集合从当前进程屏蔽的信号集中删除,相当于currmask = currmask&~set
SIG_SETMASK:表示用set替代原来的屏蔽信号集
set:表示当前要设置的屏蔽的信号集
oldset:表示设置前的屏蔽的信号集
(7)获取当前进程未决信号集
任何发送给进程没有被捕获的信号成为未决信号
,这里包含没来得及处理(正在处理其他信号)信号和因进程暂时屏蔽了这和信号而导致的未决。
int sigpending(sigset_t *set);
传出当前的未决信号集,该信号集的那个bit位为1,表示那个信号被屏蔽。
返回值:成功:0;失败:-1
③、屏蔽信号的示例
(1)示例一:
在下面的示例中,屏蔽3号信号SIGQUIT(该信号由键盘ctrl+/产生),屏蔽2号信号SIGINT(该信号由键盘ctrl+c产生),屏蔽19号信号SIGTSTP(该信号由键盘ctrl+z产生),并且验证是否能屏蔽9号信号SIGKILL(该信号通过kill -9发送产生)
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
void printsigset(sigset_t *set)
{
//64个信号太多了,只观察前32个意思意思.进程号从1开始
for (int i = 1; i <=32; i++)
{
//判断信号i是否在集合中
if(sigismember(set,i)==1)
printf("1 ");
else
printf("0 ");
}
printf("\n");
}
int main(int argc, char const *argv[])
{
sigset_t myset,oldset;
int recv = 0;
//清空myset信号集
recv |= sigemptyset(&myset);
//将要屏蔽的信号添加到信号集中
recv |= sigaddset(&myset,SIGQUIT); //ctrl+/产生
recv |= sigaddset(&myset,SIGINT); //ctrl+c产生
recv |= sigaddset(&myset,SIGTSTP); //ctrl+z产生
recv |= sigaddset(&myset,SIGKILL); //kill信号是不能被屏蔽的,这里验证是否能屏蔽
if(recv!=0)
{
perror("sidaddset fun error\n");
exit(0);
}
//获取当前未决信号集
sigpending(&oldset);
printsigset(&oldset);
//设置当前进程的屏蔽信号集
sigprocmask(SIG_BLOCK,&myset,&oldset);
sleep(9);
while(1)
{
sleep(2);
//获取当前未决信号集
sigpending(&oldset);
printsigset(&oldset);
}
return 0;
}
上面结果表明,当某个信号被事先加入到进程的屏蔽信号集中后。当该信号产生时,进程的未决信号集的相应位会被置1,同时该信号就会处于未决态。而当进程的信号集发生变化后(例如将该信号从进程的屏蔽信号集中剔除)后,刚处于未决态的信号就会被激活,继续执行该信号的动作。看下面的示例二
(1)示例二:
先将ctrl+c产生的2号信号加入到屏蔽信号集中,在程序运行后的1秒时按下ctrl+c 产生SIGINT 信号2。此时信号2并不能让进程结束,因为该信号处于未决态。4秒后程序运行到if语句中。将2号信号从进程的屏蔽信号集中剔除。此时刚刚处于未决态的信号2就会被激活,执行相应的信号处理函数。而信号2默认处理就是结束进程,因此进程自动结束 printf(“结束了\n”); 这行代码还未执行就结束了。
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void printsigset(sigset_t *set)
{
//64个信号太多了,只观察前32个意思意思.进程号从1开始
for (int i = 1; i <=32; i++)
{
//判断信号i是否在集合中
if(sigismember(set,i)==1)
printf("1 ");
else
printf("0 ");
}
printf("\n");
}
int main(int argc, char const *argv[])
{
sigset_t myset,oldset;
int recv = 0;
//清空myset信号集
recv |= sigemptyset(&myset);
//将要屏蔽的信号添加到信号集中
recv |= sigaddset(&myset,SIGINT); //ctrl+c产生
if(recv!=0)
{
perror("sidaddset fun error\n");
exit(0);
}
//设置当前进程的屏蔽信号集
sigprocmask(SIG_BLOCK,&myset,&oldset);
while(1)
{
static int j =0;
j++;
sleep(1);
//获取当前未决信号集
sigpending(&oldset);
printsigset(&oldset);
//四秒后将该信号从进程的屏蔽信号集中剔除
if(j == 4)
{
printf("进来了\n");
//将ctrl+c信号从当前进程的屏蔽信号集中剔除
sigprocmask(SIG_UNBLOCK,&myset,&oldset);
printf("结束了\n");
}
}
return 0;
}