进程信号
1. 前言
上一篇文章了解到信号产生的四种方式,
但是信号产生后,然后呢?需要对信号
进行保存,最后对信号进行处理
如果你没有阅读过前一篇文章,或者不知道信号的默认处理方式,请先阅读这篇文章: 信号的基本概念
本章重点:
2. 信号阻塞,信号递到和信号忽略
在讲解进程是如何保存信号之前,要
先了解下面几个概念:
信号递达:
信号的未决状态:
信号阻塞:
信号忽略:
阻塞和忽略的区别:
3. 进程是怎样保存信号的?
在进程的PCB中,存在三张和信号相关的表
更准确的讲,前两个结构是位图,最后一个是表(数组).
- block位图:
- pending位图:
- handler数组:
4. 信号集操作函数
首先我想隆重介绍的是signal函数:
下面可以进行一个简单的编码验证:
void mycatch(int signum)
{
cout<<"进程捕捉到了一个信号,正在自定义处理中... "<<signum<<"pid: "<<getpid()<<endl;
}
int main()
{
signal(SIGINT,mycatch);//既可以填写定义的宏,也可以直接写数字
while(1)
{
cout<<"我是一个进程,我正在运行...pid: "<<getpid()<<endl;
sleep(1);
}
return 0;
}
下面的内容能掌握的最好:
信号集操作函数概览:
读取或更改信号屏蔽字:
下面是样例代码,有兴趣可以看看:
void showpending(sigset_t& tmp)
{
for(int i=1;i<=31;i++)
{
if(sigismember(&tmp,i))
cout<<1;
else
cout<<0;
}
cout<<endl;
}
void blocksig(int sig)//对指定信号对屏蔽
{
sigset_t bset;
sigemptyset(&bset);
sigaddset(&bset,sig);
int n = sigprocmask(SIG_BLOCK,&bset,NULL);//只对sig号信号屏蔽
assert(n==0);
cout<<"block success!"<<endl;
}
int main()
{
cout<<getpid()<<endl;
for(int i=1;i<=31;i++)//将所有信号都屏蔽掉
blocksig(i);
sigset_t pending;
while(1)
{
sigpending(&pending);
showpending(pending);
sleep(1);
}
return 0;
}
5. 进程是如何捕捉信号的?
先说结论:
在此之前,大家肯定会有疑问:什么是内核态?什么是用户态?为什么这两个状态会相互切换?下面就来解答这些问题:
了解完前景知识后,我们就可以得出一些结论:
- 当程序执行系统调用时会进入到内核态
- 当执行完系统调用后,会回到用户态
- 在这期间会进行信号的检测和处理
- 如若此时检测到有信号到来,那么会把代码直接跳转到信号处理的函数处
- 当信号处理函数返回时还会执行特殊的系统调用,再回到内核态
下面可以用一张图来代表整个过程:
把图片简化一下,就得到了一个无穷大的图像:
6. 总结
信号这一章节是我们学习进程的最后一节,由于信号与进程的紧密关系,所以学习信号至关重要,除此之外,当子进程退出时也会向父进程发生SIG_CHILD信号,假设父进程并不想关心子进程的退出结果,只想执行自己的代码,那么我们可以将SIGCHLD信号设置为忽略,这样一来,父进程收到子进程退出的信号后就不会再拿一部分时间或资源来处理子进程了!