今年 408 大纲出来了,os 进程通信部分新增了信号机制,本文将梳理相关的知识点。

信号的基本概念
信号(英语:Signals)是 Unix、类 Unix 以及其他 POSIX 兼容的操作系统中进程间通讯的一种方式。它是一种异步的通知机制,用来提醒进程一个事件已经发生。当一个信号发送给一个进程,操作系统会中断进程正常的控制流程,此时,任何非原子操作都将被中断。如果进程定义了信号的处理函数,那么它将被执行,否则就执行默认的处理函数。
信号就是字面意思的信号,就是给进程发信号嘛。想给某个进程传信息就给他发个信号,进程收到信号了就进行相应的处理。很经典的一种异步通信方式。
信号类型
信号有很多种,不同体系结构对信号的定义不同。下面这些叫 SIG 啥啥啥的都是信号名。

重点注意的是 SIGKILL 和 SIGSTOP 不能被进程忽略或阻塞,但 SIGTERM 可以。
用户进程对信号的操作
1 处理信号的方法:
进程可以选择对某种信号采取的特定操作,这些操作如下:
忽略信号:进程可忽略产生的信号,但SIGKILL和SIGSTOP信号不能被忽略;
阻塞信号:进程可选择阻塞某些信号;
由进程处理的信号:进程本身可在系统中注册处理信号的处理程序地址,当发出该信号时,由注册的处理程序处理该信号;
由内核进行默认处理:信号由内核的默认处理程序处理。大多数情况下,信号由内核处理。

2 与信号有关的系统调用
注意,Linux 不区分信号的优先级。普通进程只能向同用户下的进程或同一个进程组的进程发送信号。

以上是信号逻辑上的概念,按理来说 408 考到这就差不多得了,但不排除深入考察具体实现的可能。所以下面会介绍信号相关的数据结构和内核处理过程。
PCB 中与信号相关的数据结构
PCB 中会有一个信号屏蔽字 (sigset_t blocked),进程用来设置想要阻塞的信号。有一个待决队列 (struct sigpending pending),用于保存尚未处理的信号。还有一个用于处理信号的函数指针列表 (struct sighand_struct * sighand),用于指定处理信号所用的函数。


内核处理信号的过程
1 处理信号的时机
**每次从内核返回用户进程前,会检查有没有需要处理的信号,并做对应的操作。
2 信号处理程序的执行
步骤如下:
(1) 检查对应的sigaction结构,如果该信号不是SIGKILL或SIGSTOP信号,且标有忽略,则不处理该信号。
(2) 如果该信号利用默认的处理程序处理,则由内核处理此信号,否则转向第(3)步。
(3) 该信号由进程自己的处理程序处理,内核将修改当前进程的调用堆栈帧,并将进程的程序计数寄存器修改为信号处理程序的入口地址。此后,指令将跳转到信号处理程序,当从信号处理程序中返回时,实际就返回了进程的用户模式部分。

示意图如下:


信号的处理与中断处理十分相似,都是通过内核修改进程的上下文,从而跳转到对应的处理程序。处理完成后再返回。但二者亦有区别,见下。
信号与中断(异常)的联系和区别
信号类似于中断,不同之处在于中断由 CPU 和内核的中断处理程序共同处理,而信号由内核和进程定义的函数共同处理。此外,内核还可以将中断转换为信号传递给进程。
进程的运行可能导致硬件异常,例如,将一个数除以零,或者出现 TLB 不命中。在类 Unix 系统中,这会自动运行内核的异常处理进程。对于某些异常如页缺失,内核有足够的信息来处理完并恢复进程的运行。但是对于另外一些异常,内核不能处理而只能通过发送信号把异常交给进程自己处理。例如在 x86 架构的 CPU 上,如果一个进程尝试将一个数除以零,将会产生 divide error 异常,并使内核向出错的进程发送 SIGFPE 信号。相似地,如果一个进程尝试访问虚拟地址空间以外的内存,内核将向进程发送 SIGSEGV 信号。异常与信号的具体对应关系在不同的 CPU 架构上是不同的。
有的 cpu 和 os 比较霸道,进程造成异常就直接揽过来自己给处理了。但有的呢就比较开明,或者比较懒,就把异常转换成信号,发给进程让他自己决定怎么处理。
后记
看到这个新增考点,想起来当年大二的我。当时我 os 的结课任务就是在内核里实现信号机制,为了实现一个简单的信号机制天天累死累活的 debug 却又乐在其中。最后成功实现信号机制的时候真的很开心。那个时候因为 de 出一个 bug 还在知乎发了个文章:
https://zhuanlan.zhihu.com/p/636417782
然而转眼已经准备考研大半年了,即使我已经亲手实现了信号机制,今年要是出了信号的题还不一定能做对哦,哈哈。天天搁这看书做题,连 int main 都不会敲了。逼着自己花时间学这些没用的东西真难受啊。
参考
https://zh.wikipedia.org/wiki/Unix%E4%BF%A1%E5%8F%B7
操作系统实用教程 -(第三版),任爱华等
深入 Linux 内核架构,Wolfgang Mauerer