Linux进程信号——信号处理
阻塞信号:
一、信号相关概念
1.实际执行信号的处理动作称为信号递达(Delivery).
2.信号从产生到递达之间的状态称为信号未决(Pending)。(不一定会立即Delivery)
3.进程可以选择阻塞(Block)某个信号。(不会递达)
4.被阻塞的信号产生时将保持在未决状态,知道进程解除对此信号 的阻塞,才会执行递达。
5.阻塞和忽略时不同的,信号被阻塞就不会递达,忽略是在递达之后,可选的一种处理动作。
二、在内核中的表示
该图中,每一行代表着一种信号,每一个进程PCB中都有一个信号的指针,分别由3个表控制一个信号,
1.block位图:表示每种信号是否被阻塞,0表示没有被阻塞,1表示被阻塞。
2.pending位图:表示每种信号的未决状态,1表示该信号已经已经产生,但还未递达。0表示信号没有产生,或者产生后已经递达了,此时该标志位也变为0。
3.handler表:实际是一函数指针数组,数组每个元素对应处理该信号的函数指针。如果是SIG_DEL表示自行默认处理动作,如果是SIG_IGN表示忽略该信号,如果是用户自定义处理动作,则保存的是自定义函数的指针。
所以,发送信号就可以描述为:操作系统修改进程PCB中pending表中将0变为1.
三、信号集与操作函数
1.sigset_t:未决和阻塞标志可以用相同的数据类型sigset_t来存储。这个类型可以表示每个信号有效或者无效状态。在阻塞信号中有效、无效代表着是否被阻塞,未决也是同样额,同时,阻塞信号集也称为当前进程的信号屏蔽字。
2.信号集操作函数:
#include<signal.h>
int sigemptyset(sigset_t* set);
该函数的功能是使set所指向的信号集变量的所有比特位清零,比如set所指向的是当前进程的阻塞信号集,所以在该进程中的所有信号都处于未屏蔽状态。
int sigfillset(sigset_t* set);
该函数的功能是使set所指向的信号集变量的所有比特位均变为1。比如set所指向的是当前进程的阻塞信号集,所以在该进程中的所有信号都处于屏蔽状态。
int sigaddset(sigset_t *set,int signo);
该函数的作用是在set所指向的信号集中使signo信号变为有效信号。
int sigdelset(sigset_t* set,int signo);
该函数的作用是在set所指向的信号集中使signo信号变为无效信号。
int sigismember(const sigset_t* set,int signo);
该函数的作用是判断set所指向的信号集中signo信号是否有效。
四、sigprocmask
调用函数sigpromask可以读取进程或者更改进程的信号屏蔽字。
int sigprocmask(int how,const sigset_t* set,sigset_t* oset);
参数:
oset:如果该参数非空,相当于保存该进程原本的信号屏蔽字。
set:如果该参数非空,则将根据how和set修改该进程的信号屏蔽字
how:有以下三种
SIG_BLOCK:此时set中包含的是我们希望添加到当前信号屏蔽字中的信号,相当于mask = mask|set
SIG_UNBLOCK:此时set中包含的是我们希望从当前信号屏蔽字中解除的信号,相当于mask = mask&~set
SIG_SETMASK:设置当前信号屏蔽字为set所指向的值,即mask = set。
返回值:成功返回0,失败返回-1
五、sigpending
sigprocmask告诉我们如何对阻塞信号字进行读取和修改,那sigpending将会使我们读取进程的未决信号集。
int sigpending(sigset_t* set);
读取进程的未决信号集,通过set参数传出。成功为0,失败-1.
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
void printfsigset(sigset_t *set)
{
int i = 0;
for (;i<32;++i)
{
if (sigismember(set,i))//判断信号是否在目标集合中
{
putchar('1');
}
else
{
putchar('0');
}
}
printf("\n");
}
int main()
{
sigset_t s,p;
sigemptyset(&s);//定义信号集,并清空初始化
sigaddset(&s,SIGINT);//Ctrl+c
sigprocmask(SIG_BLOCK,&s,NULL);//阻塞SIGINT信号
while(1)
{
sigpending(&p);//获取未决信号集
printfsigset(&p);
sleep(1);
}
return 0;
}
如果执行了这个代码,发现用Ctrl+c 不能发送信号退出进程,想要退出的话,找到这个进程的pid,kill掉就行。
上一篇: Cisco IOS上Segment Routing TE的简单实验
下一篇: 47、全排列II