Linux信号表是Linux系统中进程间通信和内核控制进程的重要机制,它定义了操作系统可以向进程发送的多种通知类型,每个信号都有唯一的编号和名称,用于实现进程同步、异常处理、资源管理等核心功能,理解Linux信号表不仅有助于系统管理员排查问题,也是开发人员进行底层编程的基础。

信号的基本概念与作用
信号是Linux/Unix系统中的一种异步通信方式,与管道、消息队列等同步通信机制不同,信号不需要进程通过系统调用来主动接收,而是由内核或特定事件触发后直接发送给目标进程,其核心作用包括:通知进程某事件发生(如子进程终止)、强制进程执行特定操作(如终止、暂停)、处理异常情况(如非法内存访问)等。
每个信号都有一个唯一的整数值(1-31为传统实时信号,34-64为POSIX实时扩展信号)和对应的符号名称(如SIGINT、SIGKILL),这些定义在<signal.h>头文件中,进程可以通过三种方式处理信号:忽略(但SIGKILL和SIGSTOP不可忽略)、执行默认操作(如终止进程)或自定义处理函数(通过signal()或sigaction()系统调用)。
Linux信号表的核心内容
Linux信号表根据信号特性可分为传统信号(不可靠信号)和实时信号(可靠信号),其中传统信号1-31是早期Unix系统定义的,而实时信号34-64(RTMIN到RTMAX)是后续扩展,支持信号排队和携带数据,解决了传统信号可能丢失的问题,以下对常用传统信号进行详细解析:

与进程控制相关的信号
- SIGHUP(1):挂起信号,当终端关闭或用户退出登录时,内核会向该终端下的所有进程发送此信号,常用于守护进程的重新加载配置(如nginx、sshd等进程会捕获此信号并重启自身)。
- SIGINT(2):中断信号,当用户按下
Ctrl+C时,终端驱动程序发送此信号给前台进程组,默认终止进程,多数交互式程序(如shell命令)会捕获此信号并执行清理操作后退出。 - SIGQUIT(3):退出信号,用户按下
Ctrl+\时触发,默认终止进程并生成核心转储文件(core dump),用于调试程序异常退出原因。 - SIGKILL(9):强制终止信号,不可被忽略或捕获,内核会立即终止目标进程,常用于无响应进程的强制结束。
- SIGTERM(15):终止信号,默认终止进程,但可被进程捕获并执行清理操作(如关闭文件、释放资源),是管理员优雅关闭进程的首选(如
kill命令默认发送此信号)。 - SIGSTOP(17):暂停信号(不可中断),暂停进程执行,不可被忽略或捕获,需通过
SIGCONT恢复。 - SIGTSTP(18):终端停止信号,用户按下
Ctrl+Z时触发,默认暂停前台进程(如将vim送入后台),常用于任务切换。 - SIGCONT(19):继续信号,恢复之前暂停(SIGSTOP/SIGTSTP)的进程执行。
与异常处理相关的信号
- SIGSEGV(11):段错误信号,当进程访问未授权内存地址(如野指针、数组越界)时触发,默认终止进程并生成core dump。
- SIGFPE(8):浮点异常信号,如除零错误、浮点溢出等数学运算异常时触发。
- SIGBUS(7):总线错误信号,进程访问内存地址未对齐或访问不存在的物理内存时触发(如硬件地址映射问题)。
- SIGPIPE(13):管道破裂信号,当进程向已关闭读端的管道写入数据时触发,默认终止进程(如网络编程中未处理客户端断开连接时的写操作)。
与系统资源及状态相关的信号
- SIGUSR1(10)、SIGUSR2(12):用户自定义信号,应用程序可自行定义其用途,用于进程间自定义通信(如通知子进程重新加载配置)。
- SIGCHLD(17):子进程状态改变信号,当子进程终止、暂停或恢复时,内核向父进程发送此信号,父进程可通过
wait()或waitpid()回收子进程资源(避免僵尸进程)。 - SIGALRM(14):定时器信号,由
alarm()或setitimer()设置的超时触发,用于实现定时任务或超时控制。 - SIGVTALRM(26):虚拟定时器信号,进程在用户空间运行时触发的定时器(区别于SIGALRM的实时时间)。
信号的处理机制与编程实践
信号的处理流程可概括为:内核触发信号→目标进程进入内核态→检查信号处理方式(默认/忽略/自定义)→执行对应操作(如调用信号处理函数),需要注意的是,信号处理函数的编写需遵循异步信号安全原则(避免调用非async-safe函数,如printf()、malloc()),并考虑信号屏蔽字(sigprocmask)的设置,防止竞态条件。
自定义信号处理函数的基本步骤:
#include <signal.h>
#include <stdio.h>
void sig_handler(int signo) {
if (signo == SIGINT) {
printf("Caught SIGINT!\n");
exit(0);
}
}
int main() {
if (signal(SIGINT, sig_handler) == SIG_ERR) {
perror("signal error");
return 1;
}
while(1); // 等待信号
return 0;
}
上述代码捕获SIGINT信号后,打印信息并退出,而非默认的终止行为。

对于管理员而言,可通过kill命令发送信号(如kill -9 PID强制终止进程,kill -15 PID优雅终止),或killall根据进程名批量发送信号,调试时,gdb支持信号调试(如handle SIGINT stop让调试器在捕获SIGINT时暂停)。
信号的注意事项与最佳实践
- 信号可靠性:传统信号(1-31)不支持排队,多次发送同一信号可能只被处理一次;实时信号(34-64)支持排队,适合需要可靠传输的场景。
- 不可屏蔽信号:SIGKILL和SIGSTOP无法被忽略或捕获,是内核保留的终极控制手段。
- 信号处理函数的原子性:处理函数应尽量简短,避免复杂逻辑,防止因信号嵌套导致栈溢出或死锁。
- 信号屏蔽与恢复:使用
sigaction()的sa_mask字段可临时屏蔽特定信号,处理完成后需恢复信号掩码。 - 僵尸进程处理:父进程需捕获SIGCHLD信号并在处理函数中调用
wait()回收子进程资源,避免僵尸进程占用系统PID表。
Linux信号表是操作系统与进程交互的核心接口,涵盖了从基础进程控制到异常处理的广泛场景,无论是系统运维中的进程管理,还是应用程序中的异步事件处理,信号机制都发挥着不可替代的作用,深入理解信号的含义、处理机制及编程规范,不仅能提升系统稳定性,也为复杂场景下的程序设计提供了重要工具,在实际应用中,需根据场景选择合适的信号类型,并严格遵循信号处理的最佳实践,确保系统的可靠性和安全性。
















