概述
我们知道信号的递达方式共有三种,默认,自定义捕捉和不处理,默认和不处理的过程比较简单,先不谈。本文我带大家分析一下信号捕捉的基本过程。
文章目录
- 1. 信号递达是立即发生的吗?
- 2. 用户态和内核态
- 3. 信号捕捉的基本过程
1. 信号递达是立即发生的吗?
我们来先看这样一段代码:
#include <stdio.h>
int main()
{
int arr[100];
int i = 0;
for(; i < 200; i++)
{
arr[i] = i;
}
printf("run here!?n");
return 0;
}
本段代码我定义了一个容量为100的整型数组,接着我从起始位置开始往后进行200次赋值,很显然这里发生了数组越界。数组越界属于段错误,一旦发生段错误,进程就会接收到11号信号而终止掉。我还在发生段错误之后还打印了一句话,下面我们来看运行结果:
从运行结果我们可以判断出,进程虽然接收到了11号信号,但是并没有立即执行该信号。从收到信号到执行信号中间还有一段窗口期,代码后面的printf函数正是在这段窗口期执行的。
由此我们得出结论:一个进程接收到信号之后,并不一定是立即处理的,可能会过一段时间才会处理、也就是说产生异常到处理异常中间是有时间窗口的,操作系统识别到错误后,代码可能还会向后运行一部分。
由此就引出了一个问题,进程收到信号之后会在何时处理该信号?
下面告诉大家结论:
信号的递达发生在
内核态
切换回用户态
的时候,这时会进行信号相关信息检测。
接下来我向大家介绍何谓内核态与用户态。
2. 用户态和内核态
实际上我们自己平常所写的代码在执行时,就在不断地进行用户态和内核态的切换。
对于这个过程大家平时可能没有好好的感受过,一般我们所写的代码是由自己的代码逻辑和系统调用接口组成的。当代码在调用系统调用接口时,进程就会从当前的用户态切换段内核态去执行,这个过程一般称为陷入内核。
当进程切换到内核态时,我们就可以理解为当前进程的角色就是操作系统,而不再是系统进程。
那为什么进程不直接在用户态执行系统调用接口?这里就涉及到权限的问题,系统调用接口属于操作系统的代码,进程是不能直接去执行的,进程要执行操作系统的代码,就必须切换状态,从用户态切换到内核态。
那么操作系统是怎么做到进程从用户态切换到内核态的?
这是描述进程地址空间的一张图,这张图主要可以分为两部分,内核空间占1G,用户空间占3G。有关用户空间的信息,大家可以参考本篇文章《深度理解进程地址空间》。不同进程用户空间的数据是不一样的,所以通过用户级页表映射到物理内存上的位置也是不一样的。
那么我问,不同进程内核空间的数据一不一样?
答案是一样的!
因为内核空间存放的是操作系统的代码,一个系统下只有一份操作系统的代码和数据,这也就意味着所有进程内核空间的区域划分都是一样的,内核页表的映射关系也是一样的。
由于内核空间的代码只有一份,所以当进程执行内核空间的代码时,该进程就相当于是操作系统,只不过是操作系统用了当前进程的壳而已。
那么内核态和用户态的区别是什么?
主要区别在于权限,内核态的权限大,而用户态的权限小。
3. 信号捕捉的基本过程
上面我向大家介绍了用户态和内核态的概念,下面我们来看信号捕捉处理的基本过程:
信号的捕捉过程共有5大过程。
过程1表示信号如何进入内核态。
过程2表示信号进入内核的目的
过程3表示进程从用户态向内核态切换时进程信号的检测,这里无非三种情况
- 情况1:未接收到信号,此时返回进入内核态前的上下文环境中继续执行代码,即返回过程1
- 情况2:接收到信号,但当前信号被屏蔽了,处理方法同上
- 情况3:接收到信号,信号未被屏蔽,此时执行信号的递达动作,即执行过程4
过程4:在用户态执行捕捉信号的方法,由于处理完之后还要调用系统调用接口sigreturn,于是又切换回系统态
过程5:信号的处理方法执行完之后,在返回用户态之前的上下文环境中,即返回过程1.
仔细观察上图我们会发现,信号的处理过程实际上是一个画无穷大的过程。
在交点之上画一条横线,横线之上为用户态,横线之下为内核态。横线和无穷大符号有四个交点,代表了4次状态切换。无穷大的交点处表示的就是信号的检测过程。
相信通过这张图,大家可以很轻易的记住信号处理的基本过程。
本篇文章到这里就全部结束了,希望这篇文章可以为大家带来帮助。
最后
以上就是明理钢笔为你收集整理的信号处理的基本过程1. 信号递达是立即发生的吗?2. 用户态和内核态3. 信号捕捉的基本过程的全部内容,希望文章能够帮你解决信号处理的基本过程1. 信号递达是立即发生的吗?2. 用户态和内核态3. 信号捕捉的基本过程所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复