操作系统可向进程发送信号(signal),通知异步事件,例如用户按下 Ctrl+C(SIGINT)或发生段错误(SIGSEGV)。C 程序可通过注册处理函数改变信号的默认行为,实现优雅退出或资源清理;这与错误处理、长期运行服务中的关闭逻辑相关。
信号的概念
信号是软件层面的中断通知,由内核或同机其他进程发出。每种信号对应一个编号,例如 SIGINT 在 POSIX 系统中通常为 2。
默认处置
未自定义处理时,常见处置包括忽略、终止进程或停止/继续执行。下列循环在默认情况下收到 SIGINT 会终止进程:
#include <stdio.h>
int main(void) {
while (1) {
printf("hello world\n");
}
return 0;
}注册信号处理函数
除 SIGKILL 等少数信号外,多数信号可捕获。处理函数原型为 void handler(int sig),可用 signal 注册(新代码更推荐 sigaction,可移植性与语义更明确):
signal(type, signalHandler);#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
void signalHandler(int sig) {
printf("Caught signal: %d\n", sig);
exit(sig);
}
int main(void) {
signal(SIGINT, signalHandler);
while (1) {
printf("Hello World!\n");
}
return 0;
}运行中按 Ctrl+C 将执行自定义处理逻辑而非立即采用默认终止(本例中处理函数内调用 exit)。
raise():向本进程发信号
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void signalHandler(int sig) {
printf("Interrupt handled: %d\n", sig);
exit(sig);
}
int main(void) {
signal(SIGINT, signalHandler);
raise(SIGINT);
return 0;
}kill():向指定进程发信号
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void handle_signal(int sig) {
printf("Received signal: %d\n", sig);
}
int main(void) {
signal(SIGINT, handle_signal);
pid_t pid = getpid();
kill(pid, SIGINT);
return 0;
}kill 向进程发送信号,具体效应取决于信号类型与进程是否已注册处理函数。
常见信号
| 信号 | 含义 | 默认行为 |
|---|---|---|
SIGABRT | 异常中止(abort) | 终止 |
SIGFPE | 浮点异常 | 终止 |
SIGILL | 非法指令 | 终止 |
SIGINT | 终端中断(Ctrl+C) | 终止 |
SIGSEGV | 无效内存访问 | 终止 |
SIGTERM | 终止请求 | 终止 |
SIGKILL | 强制终止 | 终止(不可捕获) |
SIGQUIT | 退出并可能生成核心转储 | 终止 |
SIGCHLD | 子进程状态改变 | 忽略或通知父进程 |
SIGCONT | 继续执行已停止进程 | 继续 |
SIGTSTP | 终端停止(Ctrl+Z) | 停止 |
实现层面的注意点
- 信号可能处于待处理状态,直至进程从可中断的系统调用返回后再递送。
- 可在临界区暂时阻塞特定信号(信号掩码)。
- 处理函数在独立上下文中执行,仅应调用异步信号安全函数;复杂清理宜通过标志位在主循环中完成。
本节介绍了信号的默认行为与自定义处理。下一节概述 POSIX 线程(多线程)的基本 API。