信号的处理方式与生命周期(核心机制篇)

信号的基本共识

首先需要明确一个基本共识:信号是发送给进程的事件通知机制

在 Linux/Unix 系统中,可以通过命令:

  • kill -9 PID
  • kill -SIGINT PID
    向指定 PID(Process ID) 的进程发送信号。
    因此可以确认:

信号的接收对象是进程。


进程如何识别信号

类比人识别红绿灯需要:

  1. 能够识别信号
  2. 知道对应行为
    进程识别信号同样需要两个要素:

识别信号

进程必须能够识别某种信号类型,例如:

  • SIGINT
  • SIGTERM
  • SIGKILL
    由于进程本质是程序代码的执行实例,因此:

进程对信号的识别能力是由程序员通过程序代码实现的。

换句话说:
信号识别逻辑由程序设计实现。


对信号产生行为

当进程识别到信号后,需要执行相应行为,例如:

  • 终止进程
  • 忽略信号
  • 执行信号处理函数
    这意味着:

每种信号必须定义对应的处理策略。

信号不一定被立即处理

当进程收到信号时,进程可能正在执行其他代码,因此:

信号不一定会被立即处理。

原因:

  • 进程可能正在执行关键代码
  • 当前代码优先级更高
  • 系统调度策略影响
    因此:
    信号处理具有延迟性。

信号必须被暂存

由于信号可能无法立即处理,因此系统必须解决一个问题:

信号到达后,在被处理之前如何保存?

因此进程必须具备:
信号暂存能力

也就是说:
信号需要被记录下来,等待后续处理。


信号在进程中的存储位置

进程在操作系统中由 PCB(Process Control Block) 描述。

在 Linux 中,PCB 对应的数据结构是:

复制代码
task_struct

因此:

信号状态会被保存在进程的 task_struct 中。


信号的存储方式(位图)

Linux 使用 位图(bitmap) 记录信号状态。

普通信号编号:

复制代码
1 ~ 31

因此可以使用一个 32位整数 来表示:

c 复制代码
struct task_struct {
    ...
    unsigned int signal;
    ...
};

位图规则:

位位置 表示信号
bit1 signal1
bit2 signal2
bit3 signal3
... ...
bit31 signal31
位值含义:
位值 含义
0 未收到信号
1 已收到信号
例如:
复制代码
00000000000000000000000000001000

表示:

复制代码
收到了 signal4

信号发送的本质

教材中通常称为:

发送信号 (send signal)

但从系统实现角度来看:
信号发送的本质是修改进程PCB中的信号位图。

例如:

发送 signal9

复制代码
bit9 = 1

即:

复制代码
signal_bitmap |= (1 << 9)

因此:

信号发送 ≈ 修改 PCB 中的信号标志位。

并不是真正意义上的"传输数据"。


谁有权限修改信号位图

需要注意:

  • PCB 是 内核数据结构
  • task_struct操作系统维护
    因此:

用户程序没有权限直接修改 PCB。

只有:
操作系统内核

才能修改进程的信号位图。


发送信号的最终结论

因此可以得到一个重要结论:

所有信号发送行为,本质上都是操作系统内核修改目标进程PCB中的信号状态。

即:

复制代码
用户程序
    ↓
系统调用
    ↓
操作系统内核
    ↓
修改目标进程 PCB 信号位图

进程处理信号的三种方式

当进程处理信号时,可以采取三种策略:

默认处理(Default Action)

操作系统定义默认行为,例如:

  • 终止进程
  • 产生 core dump
  • 忽略

捕捉信号(Signal Catch)

进程可以注册:

复制代码
signal handler

即:
自定义信号处理函数


忽略信号(Ignore)

进程可以选择:

复制代码
忽略该信号

例如:

复制代码
SIG_IGN

信号发送

在 Linux / Unix 系统中,无论信号通过何种方式产生,其本质都是:

由操作系统内核向目标进程设置相应的信号状态。

原因如下:

  1. 进程的控制信息存储在 PCB(Process Control Block)
  2. Linux 中 PCB 对应的数据结构是:
c 复制代码
task_struct
  1. PCB 属于 内核数据结构
    因此:

用户程序没有权限直接修改 PCB。

只有:
操作系统内核(Kernel)

才能修改进程的信号状态。


用户发送信号必须依赖系统调用

由于用户程序不能直接访问内核数据结构,因此如果希望用户程序能够发送信号,就必须通过:

系统调用(System Call)

来完成。

因此操作系统必须提供一组 信号相关系统调用接口,用于:

  1. 发送信号
  2. 设置信号处理方式
  3. 处理信号
    例如常见接口:
系统调用 作用
kill() 向进程发送信号
signal() 设置信号处理方式
sigaction() 更规范的信号处理接口
raise() 向当前进程发送信号

kill 命令的底层实现

在 Linux 中常用命令:

bash 复制代码
kill -9 PID

虽然这是一个 shell 命令,但其本质是:

调用系统调用 kill()

执行流程如下:

复制代码
用户命令 kill
        ↓
shell 调用 kill() 系统调用
        ↓
内核修改目标进程 PCB 信号位图
        ↓
目标进程收到信号

因此可以得出结论:

所有信号发送最终都由操作系统内核完成。

相关推荐
特种加菲猫2 小时前
C++ 容器适配器揭秘:stack, queue 和 priority_queue 的模拟实现
开发语言·c++
小此方2 小时前
Re:Linux系统篇(二)指令篇 · 一:基础六大指令精讲+Linux操作技巧——让你从小白到入门
linux·服务器
Shingmc32 小时前
【Linux】Socket编程TCP
服务器·网络·tcp/ip
xixixi777772 小时前
Gartner 2026核心趋势:前置式主动安全(PCS)成为安全战略新范式,量子安全+国密算法构筑政企纵深防御底座
网络·人工智能·安全·web安全·ai·量子计算
SilentSamsara2 小时前
ConfigMap 与 Secret:配置注入的四种姿势与安全边界
linux·运维·服务器·安全·微服务·kubernetes·k8s
飘忽不定的bug2 小时前
记录:RK3576 适配开源GPU驱动(panfrost)
linux·gpu·rk3576·panfrost
Lentou2 小时前
部署项目之systemd部署
linux·运维·服务器
郝学胜-神的一滴2 小时前
[力扣 105]二叉树前中后序遍历精讲:原理、实现与二叉树还原
数据结构·c++·算法·leetcode·职场和发展
鄃鳕2 小时前
ubuntu下将DHCP动态分配改成静态ip
linux·tcp/ip·ubuntu