信号

信号是进程级异步事件通知机制。其目的是在目标进程的控制信息中标记某类事件发生。

900

信号机制的核心是三类控制信息:

信息 含义
待处理信号集 哪些信号已经到达,但还没有被处理
信号屏蔽字 哪些信号暂时被屏蔽,暂不投递给进程处理
信号处理动作表 每种信号采用默认处理、忽略,还是调用用户注册的处理函数

每种信号在目标进程 PCB 中表示为 1 bit:某位为 1 表示该类信号处于待处理状态。

一次典型信号处理过程:

  1. 信号产生。来源可能是终端输入、定时器、异常、I/O 状态变化,或其他进程请求发送信号。
  2. 内核记录信号。内核在目标进程的待处理信号集中设置对应位。
  3. 检查屏蔽字。若该信号被屏蔽,暂不处理;若未屏蔽,等待合适时机投递。
  4. 返回用户态前检查。进程因系统调用、异常、中断等原因进入内核态后,内核在返回用户态前检查是否有未屏蔽的待处理信号。
  5. 执行处理动作。内核根据该信号的处理动作表,执行默认动作、忽略,或安排用户处理函数运行。
  6. 处理结束后继续。若信号没有终止进程,处理完成后回到原进程的正常执行流。
为什么返回用户态前检查信号

信号处理需要内核介入,但用户自定义处理函数属于用户程序逻辑。进程进入内核态后,内核正好有机会检查待处理信号,并在返回用户态时把控制流切到用户注册的处理函数;处理函数返回后,再恢复到原本应继续执行的位置。

不是所有信号都能被捕获或忽略。`SIGKILL` 和 `SIGSTOP` 通常不能被进程改写处理方式,否则进程可能拒绝被系统终止或暂停。

Linux 中用 killpkill 发送信号的命令用法见 Linux Signals

信号和异常/中断

信号可以作为异常和中断处理之后的用户态通知方式。

来源 内核处理 可能转化出的信号
终端产生中断事件 终端驱动和内核处理控制字符 SIGINT
定时器到期 内核处理定时器中断或定时事件 SIGALRM
子进程退出 内核更新子进程状态,通知父进程 SIGCHLD
用户程序访问非法地址 CPU 触发异常,内核判断无法继续正常执行 SIGSEGV
算术异常 CPU 触发异常,内核定位到当前进程 SIGFPE

异常/中断首先让 CPU 转入内核态,由内核完成硬件相关或系统级处理。若这个事件需要通知某个用户进程,内核可以在该进程的待处理信号集中设置对应信号。之后,当该进程准备回到用户态时,信号机制再负责把这个事件交给进程处理。

异常与中断的基本机制见 Exception-And-Interrupt-BriefException-And-Interrupt-Handling

信号不是信号量

信号用于异步通知事件,例如终止、子进程退出。信号量用于同步、互斥或资源计数,例如缓冲区是否有空位、临界区是否可进入。二者名字相近,但用途完全不同。