编程与 AI13 分钟阅读更新于 2026-08-04

Linux 信号是什么:异步通知、阻塞与进程状态变化

围绕 Linux 信号产生、传递、处理、阻塞和未决状态,解释信号如何通知进程发生了终端操作、子进程退出或异常事件。

相关工具

信号是一种异步通知

进程通常按照自己的执行顺序向前运行,但系统中总会出现一些需要立即告知它的事件:用户在终端按下中断键,子进程结束,程序访问了无效内存,或者定时器到期。信号可以理解为操作系统发给进程的一种短通知,让进程知道某个事件已经发生。

它与普通数据传输不同。信号通常只携带有限的事件类型,不负责承载大块业务数据;它更像门铃,而不是信件。进程收到信号后,可以按照默认规则结束、暂停、继续,也可以在允许的情况下设置自己的处理方式。

Linux 信号从事件产生到进程处理的流程图
信号从事件到处理

终端、硬件、内核或其他进程产生事件,系统把它转换为信号并交给目标进程。

信号从哪里产生

信号可能来自用户操作。例如用户按下 Ctrl+C,终端会向前台任务发送 SIGINT,请求它中断运行。也可能来自硬件或处理器发现的异常,例如进程访问无效内存时产生段错误信号。系统调用、定时器和其他进程也可以成为信号的来源。

子进程状态变化是另一类常见来源。子进程结束、暂停或继续运行时,父进程可能收到 SIGCHLD。父进程据此知道需要检查子进程状态并回收相应资源。信号本身只说明事件发生,具体状态仍需要通过进程管理机制进一步确认。

一个信号会经历什么状态

从产生到被处理,信号通常会经过几个阶段。事件发生后,系统把信号标记为已经产生;如果目标进程当前允许处理,它会进入递达状态并执行默认动作或自定义处理;如果进程暂时屏蔽了该信号,信号会处于未决状态,等待之后解除屏蔽。

未决并不等于信号被永久保存。不同信号的合并规则和实时性要求可能不同,普通信号通常只需要表达“这类事件已经发生”,而不是记录事件发生了多少次。因此不能把普通信号当作可靠消息队列使用,需要传递详细数据时应选择合适的通信机制。

Linux 信号产生、阻塞、未决和处理状态的流程图
信号的生命周期

信号产生后,根据进程当前的屏蔽状态进入处理流程或暂存为未决信号。

默认处理、忽略与自定义处理

每种信号都有系统规定的默认动作。默认动作可能是终止进程、暂停进程、继续运行,或者忽略该信号。这样的规则让许多常见事件不需要进程额外准备就能得到基本处理,例如终端中断通常会让前台任务结束。

对于部分信号,进程可以选择忽略或设置处理逻辑,让行为更符合自己的任务需要。但也有少数信号不能被忽略或捕获,例如 SIGKILL 和 SIGSTOP,它们为系统保留了可靠地终止或暂停进程的手段。这个限制避免进程通过修改自身规则而完全失去控制。

几个常见信号分别表示什么

SIGINT 通常对应终端的中断操作,表示用户希望前台任务停止;SIGQUIT 也来自终端,但默认处理可能留下用于诊断的核心文件。SIGSEGV 表示进程进行了无效的内存访问,通常意味着程序出现了严重错误。SIGPIPE 常见于向已经没有读取端的管道写入数据。

SIGCHLD 与父子进程管理关系密切:子进程结束、暂停或继续运行时,父进程可能收到这个信号。它不是替代 wait 的详细报告,而是提醒父进程“子进程状态可能发生了变化”。父进程仍然需要检查具体状态并完成清理,否则结束的子进程可能暂时留下僵尸状态。

阻塞信号是为了保护关键阶段

进程可以暂时把某些信号加入阻塞集合。在屏蔽期间,信号不会立即执行处理动作,而是保留为未决状态,等解除屏蔽后再决定如何处理。这样做适合保护一段不能被异步打断的关键操作,例如更新一组必须保持一致的状态。

阻塞不是删除,也不是永久忽略。解除屏蔽后,未决信号仍可能被递达。设计这类流程时,要控制阻塞时间,避免进程长时间收不到重要通知;还要区分“信号已经产生”和“处理函数已经执行”,两者之间可能存在间隔。

Linux 信号屏蔽集合和未决信号集合关系图
阻塞与未决信号

信号在阻塞期间暂存为未决状态,解除屏蔽后才进入处理阶段。

SIGCHLD 与僵尸进程

子进程结束后,内核还需要保留少量信息,例如进程 ID、退出状态和消耗的处理器时间,供父进程查询。在父进程完成查询前,子进程可能处于僵尸状态。僵尸进程已经不再运行,却仍保留着内核中的进程记录,因此大量积累会占用系统资源。

SIGCHLD 可以让父进程及时知道子进程状态变化,随后通过等待和回收动作读取退出信息并释放记录。如果父进程本身提前结束,子进程会被重新交给系统中的专门管理进程负责后续处理。理解这一过程后,就能区分“进程已经结束”和“进程记录已经被回收”这两个不同阶段。

信号适合通知,不适合传输复杂数据

信号的优势在于轻量、异步和触达迅速。它适合表达“停止”“继续”“子进程状态变化”“定时器到期”这类事件,让进程从当前执行路径中获得提醒。信号处理也必须尽量简单,因为处理发生在原有执行流程之外,过于复杂的工作容易和进程正在进行的操作互相影响。

如果任务需要传递长文本、文件内容或结构复杂的数据,应使用共享存储、消息队列、管道等通信方式。可以把信号看成通信系统中的控制提示,把数据本身放在更适合的通道里。这样既保留了信号的及时性,也避免把它承担不了的工作交给信号机制。

用一条主线理解 Linux 信号

Linux 信号是一种由事件触发的异步进程通知:事件可能来自终端、硬件、内核、定时器或其他进程;信号产生后,可能立即递达,也可能因为屏蔽而暂时处于未决状态;递达时,进程按照默认动作或允许的自定义规则处理。

学习信号时,依次问四个问题就够了:谁产生了信号,信号通知的是什么事件,进程当前是否屏蔽它,处理完成后还需要回收或确认什么资源。沿着这条线,终端中断、内存异常、子进程退出和僵尸进程都会落回进程状态管理这一条主线上。

常见问题

信号和消息队列有什么区别?

信号主要表达一个需要注意的事件,携带的数据很少;消息队列用于保存和传递具体消息,适合传递结构化内容,但需要承担队列容量和数据复制等管理成本。

阻塞信号是不是等于忽略信号?

不是。阻塞只是暂时延后处理,信号可能保留为未决状态;忽略则是不执行该信号的默认动作或自定义处理。

子进程结束后为什么还可能存在?

子进程的执行已经结束,但父进程还没有读取退出状态并回收对应的进程记录时,它可能暂时处于僵尸状态。