在讲信号时,我们讲过信号是一种软中断,关于中断的话题我觉得值得用单独一篇博客来讲讲。
硬件中断:从操作系统开始
操作系统的运行本质就是靠中断来做到的,可见中断的重要性,中断分为硬件中断 ,时钟中断 ,软中断。
由外部设备触发的,中断操作系统运行流程,叫做硬件中断。我们通过一张图来解释中断是如何使操作系统运行起来的:

第一步 :外部设备,诸如:键盘,磁盘等,它们的信息准备好,就会向中断控制器发送中断信号。
第二步:中断控制器也是一个硬件,它会汇总所有外设的中断请求,做出一些处理之后,向CPU发送通知,告诉CPU:有人请求中断啦!
第三步:CPU得知有中断,从中断控制器获取中断号(后面会用上),CPU 响应中断时,会先自动把当前程序的上下文(寄存器、程序计数器等)压入栈中保存,这就是 "保存现场",处理完中断后再恢复。
第四步 :CPU根据中断号,在中断向量表(操作系统初始化时就已经建立)中查找对应的中断方法并执行。
第五步:处理完毕后,CPU 恢复之前保存的现场,回到被打断的程序继续执行。
以上是一次硬件中断的过程,而操作系统在你的电脑开机时就会陷入一个死循环,循环地等待中断,然后处理中断,再恢复执行,这就是操作系统运行的底层逻辑。操作系统通过中断实现对所有硬件设备的调度和管理。
时钟中断
上述的硬件中断,是由硬件主动触发的,还有一种定期触发的中断叫做时钟中断,有一种美丽的说法把它叫做系统的 "心跳"。
时钟中断会强制 CPU 定期停下当前进程,让操作系统有机会切换到其他进程,实现并发执行,避免单个进程一直占用 CPU。本质是给进程分配时间片,我们可以把它看作一个计数器,当时间片耗尽,也就是计数器的数字减小到 0 时,进程终止。
你或许听说过:主频越快,CPU越快。会想到:主频是不是和时钟中断有关呢?
很遗憾,主频和时钟中断没有直接关系。但是CPU 主频决定了两次时钟中断之间,进程能真正执行多少指令:比如主频 3GHz 的 CPU,每秒可执行约 30 亿条指令;而常见的时钟中断频率为 100Hz(每 10ms 触发一次),在这 10ms 的时间片里,CPU 大约能执行 3000 万条指令。主频越高,同一段时间片内进程的实际运行效率越高,这就是为什么"主频越快,CPU越快",但时钟中断本身会带来调度开销,因此中断频率也不是越高越好。
软中断
通过前面对中断的讲解,你想必已经发现中断和信号十分类似,因为信号本身就是一种中断。
而不管是硬件中断还是时钟中断,都是硬件设备触发的,而软中断可以因为软件原因触发。除了我们说过的信号是软中断,其实系统调用的本质也是触发软中断,之后CPU就会自动执行系统调用的处理方法,而这个方法会根据系统调用号,自动查表,执行对应的方法,所以,系统调用号的本质就是数组下标。
同为软中断,int 0x80 和 syscall 是软中断的底层指令,它们被成为 陷阱(主动触发) ,而除零/野指针这种叫做 异常(被动触发)。还记得之前学进程时就讲过的缺页中断吗,它也是异常的一种,所以缺页中断也叫做缺页异常。
信号捕捉过程的用户态和内核态转换
了解了中断的知识,我们现在可以讲讲一个信号是如何被捕捉的了。但在这之前,我们再补充两个概念:
用户态:程序的「平民身份」,权限受限,不能直接碰硬件、内核数据。
内核态:程序的「管理员身份」,拥有最高权限,能操作所有硬件和内存。
之所以要分两种状态,核心目的是保护系统的安全与稳定,因为如果随便一个程序都可以直接操作硬件/内核数据,是很危险的!
回到主要话题:信号被捕捉的过程是怎样的?
我们依然是先看图,再解释:

图中一个倒着的数字8,一条直线划分用户态 和内核态 ,直线和"8"的交点 就是切换一次,所以整个进程捕捉过程,会进行 4次 "态" 的切换。
然而除了这4个交点,还有一个数字"8"自己的交点,虽然标记为 检查pending表 的地方,但是实际上只在线第一次经过时检查,之所以把它标在这,是对其"关键性"的修饰。
起始点(用户态):程序正常运行,在用户态执行代码。
第一次切换(用户态 → 内核态):收到信号 / 系统调用,进入内核态处理中断 / 异常。
内核态处理(检查 pending 表):内核检查进程的信号 pending 表,发现有需要处理的信号。
第二次切换(内核态 → 用户态):为了执行用户注册的信号处理函数,切回用户态运行处理函数。
第三次切换(用户态 → 内核态):信号处理函数执行完毕,触发返回机制,再次进入内核态收尾。
第四次切换(内核态 → 用户态):内核恢复进程上下文,切回用户态,回到原来被打断的代码继续执行。