信号(Signal)** 是一种进程间异步通信机制,用于通知进程发生发生了某种事件(如错误、用户中断等)
在 Unix/Linux 系统中,信号(Signal) 是一种进程间异步通信机制,用于通知进程发生发生了某种事件(如错误、用户中断等)。信号可以由内核、其他进程或进程自身发送,接收信号的进程会中断当前操作,转而去执行预设的处理函数(或默认行为)。
信号的核心特性
- 异步性:信号的产生和传递是不确定的,进程无需主动等待,会在信号到达时被打断。
- 事件驱动:每种信号对应特定事件(如
SIGINT
对应 Ctrl+C 中断,SIGSEGV
对应段错误)。 - 预设处理方式:每个信号有默认处理行为(终止、忽略、暂停等),也可自定义处理函数。
常见信号及其含义
信号名 | 编号 | 含义及触发场景 | 默认行为 |
---|---|---|---|
SIGINT | 2 | 用户按下 Ctrl+C 中断进程 | 终止进程 |
SIGTERM | 15 | 请求进程终止(kill 命令默认发送) | 终止进程 |
SIGKILL | 9 | 强制终止进程(不可捕获/忽略) | 终止进程 |
SIGSEGV | 11 | 段错误(访问无效内存地址) | 终止进程并生成核心转储 |
SIGPIPE | 13 | 向已关闭的管道写入数据 | 终止进程 |
SIGALRM | 14 | 定时器超时(alarm() 函数触发) | 终止进程 |
SIGCHLD | 17 | 子进程终止或状态改变 | 忽略 |
信号处理函数
进程可以通过 signal()
或 sigaction()
函数自定义信号的处理方式(信号捕获)。
1. signal()
函数(简单但功能有限)
#include <signal.h>// 信号处理函数原型:参数为信号编号
void (*signal(int signum, void (*handler)(int)))(int);
signum
:要处理的信号(如SIGINT
)。handler
:处理函数指针,或特殊值:SIG_IGN
:忽略该信号。SIG_DFL
:恢复默认行为。
2. sigaction()
函数(更灵活,推荐使用)
#include <signal.h>int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);// 信号处理结构体
struct sigaction {void (*sa_handler)(int); // 处理函数sigset_t sa_mask; // 处理信号时阻塞的其他信号int sa_flags; // 标志(如 SA_RESTART 重启被中断的系统调用)
};
示例代码:捕获 SIGINT
信号
#include <stdio.h>
#include <signal.h>
#include <unistd.h>// 自定义信号处理函数
void handle_sigint(int signum) {printf("\n收到 SIGINT 信号(Ctrl+C),程序不会退出!\n");
}int main() {// 注册 SIGINT 信号的处理函数struct sigaction sa;sa.sa_handler = handle_sigint; // 设置处理函数sigemptyset(&sa.sa_mask); // 不阻塞其他信号sa.sa_flags = 0; // 默认标志if (sigaction(SIGINT, &sa, NULL) == -1) {perror("sigaction failed");return 1;}printf("程序运行中,按 Ctrl+C 测试信号处理...\n");while (1) {sleep(1); // 无限循环等待信号}return 0;
}
运行效果:
按下 Ctrl+C 后,程序不会终止,而是执行 handle_sigint
函数并继续运行(默认行为被覆盖)。
信号发送与处理流程
-
信号发送:
可通过kill()
或pthread_kill()
向进程发送信号:#include <sys/types.h> #include <signal.h>// 向进程 pid 发送 signum 信号 int kill(pid_t pid, int signum);
例如:
kill(12345, SIGTERM)
向 PID 为 12345 的进程发送终止请求。 -
信号处理流程:
- 信号产生后,内核将其标记到目标进程的信号掩码中。
- 进程从内核态返回用户态时,检查是否有未处理的信号。
- 若有,暂停当前执行,转而去执行该信号的处理函数。
- 处理完成后,回到被中断的位置继续执行。
注意事项
-
不可靠信号与可靠信号:
- 早期 Unix 信号(如
SIGINT
、SIGTERM
)为不可靠信号(编号 < 32),可能丢失且不支持排队。 - POSIX 扩展的实时信号(
SIGRTMIN
至SIGRTMAX
)为可靠信号,支持排队和携带附加数据。
- 早期 Unix 信号(如
-
信号安全函数:
信号处理函数中应使用异步信号安全函数(如write
、_exit
),避免调用printf
、malloc
等非安全函数(可能导致死锁)。 -
SIGKILL
与SIGSTOP
:
这两个信号不可捕获、不可忽略、不可自定义处理,用于强制终止或暂停进程(系统级控制)。 -
信号屏蔽:
可通过sigprocmask()
暂时屏蔽某些信号,防止其在关键操作(如数据更新)中打断进程。
应用场景
- 进程终止控制(如
kill
命令发送SIGTERM
)。 - 异常处理(如捕获
SIGSEGV
记录崩溃信息)。 - 用户交互(如 Ctrl+C 中断程序)。
- 进程间通知(如子进程结束后通过
SIGCHLD
通知父进程)。
信号是 Unix 系统中最基础的异步通信机制,广泛用于处理突发事件和进程控制,但需注意其异步性带来的复杂性(如数据一致性问题)。