信号
信号和信号量
信号量( Semaphore ) —— 实现进程间的同步、互斥
信号( Signal ) —— 实现进程间通信( IPC , Inter Process Communication )
考研大纲将信号定义为一种进程间的通信方式
信号的作用
信号( signal ):用于通知进程某个特定事件已经发生。进程收到一个信号后,对该信号进行处理
注意
- 不同的操作系统对信号类型的定义不一样
- 通常用宏定义常量表示信号名,如:
#define SIGINT 2
信号的实现原理
发送与保存
在进程 PCB 中通常会维护一张位向量表,用于表示当前未处理的信号
其结构大概如此:
一个信号类型对应的一个待处理信号 / 被阻塞信号占 1 位
| 信号类型 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | …… |
|---|---|---|---|---|---|---|---|---|
| 待处理信号 pending | 0 | 0 | 0 | 0 | 0 | 0 | 0 | …… |
| 被阻塞信号 blocked | 0 | 0 | 0 | 0 | 0 | 1 | 1 | …… |
blocked 也可以被称为信号掩码,对应信号位为 1 代表该进程阻塞(屏蔽)该种信号,表格中为 6 、 7 号信号被屏蔽
进程可以通过系统调用来设置信号掩码
用户进程和内核进程都可以使用 int kill(pid_t pid, int sig) 来向用户进程发送信号,但用户进程发送的信号类型是有限制的
- pid :进程 ID
- sig :信号类型
向对应进程发送该指令,该进程会将向量表内的 pending 对应信号位设置为 1
如果多个 kill 指令到达同一个进程,该进程只会记录一次,后续的 kill 指令不会再改变 pending 位
处理
- 何时处理信号
当进程从内核态转为用户态时(如:系统调用返回、或中断处理返回时),例行检查是否有待处理信号,如果有,就处理信号
- 处理哪种信号
在处理信号前,先检查信号是否被阻塞
将 blocked 位向量取出全部取反,与 pending 位进行与操作,对于结果为 1 的位对应的信号进行处理,其他则处理
用上面的例子解释:
- 假设某进程向该进程传递 1 和 7 号信号
- 该进程的 blocked 位向量为 0000011,pending 位为 1000001
- blocked 取反后为 1111100
- 与 pending 位进行与操作,1111100 & 1000001 = 1000000
- 结果为 1000000
- 因此,该进程会调用对应处理 1 号信号的方法处理 1 号信号
- 如何处理信号
① 执行操作系统为此类信号设置的缺省(默认)信号处理程序(某些信号默认忽略,不作处理)
② 执行进程为此类信号设置用户自定义信号处理程序(自定义信号处理程序将覆盖 ① )
信号处理程序运行结束后,通常会返回进程的下一条指令继续执行(除非信号处理程序将进程阻塞或终止)
一旦处理了某个信号,就将 pending 位重置为 0
重复收到的同类信号,将被简单地丢弃(因为仅有 1 bit 记录一类待处理信号)
当同时收到多个不同类信号时,通常先处理序号更小的信号
各个进程的信号处理有何区别?
对信号的阻塞不同
对信号的自定义处理不同
各自对信号的处理互相独立不受影响
信号和异常的关系
“信号”可以作为“异常”的配套机制,让进程对操作系统的异常处理进行补充
在进程运行过程中,某些特殊事件可能引发“异常”,操作系统内核负责捕获并处理异常
有些异常可以由内核完成全部处理(如:缺页异常),此时就不必再使用信号机制。
有些异常无法由内核完成全部处理,可能还需要用户进程配合,此时就可以用“信号机制”与“异常机制”相互配合(如:在 Linux 中,发生除以 0 异常时,内核的异常处理程序会向用户进程发送 SIGFPE 信号。SIGFPE 信号的默认处理程序会将进程终止并转储内存;当然进程可以自定义 SIGFPE 信号处理程序)
注意
有些信号不允许用户进程自定义处理程序,也不能被阻塞,如:
- SIGKILL
- SIGSTOP
更新日志
e023d-于
