# Signal信号

:::{note}
本文Maintainer: 龙进

Email: <longjin@RinGoTek.cn>
:::

&emsp;&emsp;信号是一种进程间通信机制，当我们发送一个信号给特定的进程，就能触发它的特定行为（例如退出程序，或者运行一些信号处理程序）。信号是发送到进程或同一进程内的特定线程的异步通知，用于通知它有事件发生。信号的常见用途是中断、挂起、终止或终止进程。发送信号时，操作系统会中断目标进程的正常执行流程以传递信号。可以在任何非原子指令期间中断执行。如果该进程之前注册了一个信号处理程序，则执行该例程。否则，将执行默认信号处理程序。

&emsp;&emsp;信号类似于中断，区别在于中断由CPU调解并由内核处理，而信号在内核中产生（也可通过系统调用让内核产生）并由各个进程或内核的默认处理函数处理。

## 1.信号处理概要

### 1.1 信号发送

&emsp;&emsp;当进程A想发送信号给进程B的时候，使用`kill(pid, signal)`接口进行发送。然后陷入内核的`sys_kill()`函数中进行处理。然后内核将会把信号加入目标进程的pcb的`sigpending`中。

示意图如下：

```text
   ┌────────────┐
   │ Process A: │
   │            │
   │  sys_kill  │
   └──────┬─────┘
          │
          │
   ┌──────▼──────┐    ┌────────────────────┐
   │ Send Signal ├────►Add to sigpending of│
   └─────────────┘    │   process B.       │
                      └────────────────────┘

```

### 1.2 信号处理

&emsp;&emsp;进程会在退出内核态的时候，跳转到`do_signal()`函数内，检查当前是否有需要被处理的信号，如果有的话，就会开启信号处理流程。

信号处理流程示意图：

```text

 ┌───────────────────────┐
 │       Process B:      │
 │                       ◄─────────────────────────────────┐
 │ Return from syscall...│                                 │
 └─────────┬─────────────┘                                 │
           │                                               │
           │                                               │
           │                ┌────────────────┐             │
     ┌─────▼─────┐ default  │                │             │
     │ do_signal ├────────► │ stop process B.│             │
     └─────┬─────┘  action  │                │             │
           │                └────────────────┘             │
           │ custom action                                 │
    ┌──────▼───────┐                                       │
    │ setup signal │                                       │
    │    frame     │                                       │
    └──────┬───────┘                                       │
           │jump to                                        │
    ┌──────▼───────┐ ┌────────────┐ sys_sigreturn ┌────────┴────────┐
    │   userland   ├─►sa_restorer ├──────────────►│Restore the stack│
    │ sig handler  │ └────────────┘               │    frame.       │
    └──────────────┘                              └─────────────────┘

```

- 如果内核检查发现，进程没有指定信号处理函数，且信号处理动作不是“忽略”，那就会杀掉进程。
- 如果内核发现该信号没有被忽略，那么将会：
    - 备份当前内核栈
    - 设置信号处理的用户态栈帧
    - 回到用户态，执行信号处理函数
    - 信号处理函数结束之后，将会进入由libc提供的__sa_restorer中，发起`sys_sigreturn()`系统调用，回到内核态
    - 内核恢复处理信号之前的内核栈。
    - 信号处理流程结束，内核继续执行“返回用户态”的过程。
- 如果内核发现当前信号被忽略，那么就检查下一个信号。
- 发现没有任何需要处理的信号时，返回用户态。

## 2. 其他问题

### 2.1 锁顺序约定（重要）

&emsp;&emsp;为避免信号相关路径出现锁顺序反转导致的死锁，必须遵循固定的锁顺序：  
**先获取 sighand（共享信号状态），再获取 sig_info（线程私有信号状态）**。  
任何路径都不得在持有 sig_info 锁时进入 sighand 锁。  
如需同时访问两者，请按上述顺序获取，并保持访问窗口尽可能短。
