优雅退出需用 volatile sig_atomic_t 标志配合 sigaction 注册信号处理器,在循环中检查标志并执行清理;禁用 signal(),避免信号处理器中非异步安全操作,超时阻塞调用确保及时响应。

在无限循环中优雅处理退出信号,关键不是“打断循环”,而是让循环主动感知退出请求并安全收尾。核心思路是:用全局标志位控制循环,配合可捕获信号(如 SIGTERM、SIGINT)修改该标志,主逻辑在每次迭代检查它。
设置信号处理器并声明原子标志
必须使用 volatile sig_atomic_t 类型声明标志变量,确保信号中断时的读写不会被编译器优化或撕裂。同时用 sigaction() 替代老旧的 signal(),避免竞态和不可靠行为。
- 包含头文件:
#include <signal.h></signal.h>、#include <unistd.h></unistd.h> - 定义标志:
volatile sig_atomic_t keep_running = 1; - 编写处理器函数,只做最简操作:
keep_running = 0; - 注册处理器:
struct sigaction sa; sa.sa_handler = signal_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; sigaction(SIGTERM, &sa, NULL);
主循环里持续检查退出状态
循环体本身不阻塞在信号上,而是以非阻塞或带超时的方式运行任务,每轮开始或结束前检查 keep_running。这样既能响应信号,又不会卡死在某个系统调用里。
- 避免写
while(1) { ... }这种纯忙等;改用while (keep_running) { do_work(); sleep(1); } - 如果循环内有阻塞调用(如
accept()、read()),需设置超时或使用select()/poll(),确保能定期返回检查标志 - 不要在信号处理器里做日志、文件 I/O、内存分配等非异步安全操作
退出前执行清理逻辑
循环退出后,立即进入清理阶段。这是“优雅”的真正体现——释放资源、保存状态、关闭连接。
- 关闭打开的文件描述符、网络 socket、数据库连接
- 删除临时文件或目录:
unlink("/tmp/myapp.pid"); - 等待子进程结束:
waitpid(-1, NULL, WNOHANG);或循环处理 SIGCHLD - 向父进程或监控系统发送确认信号(如通过管道写入“done”)
测试与验证要点
仅写代码不够,必须验证是否真能优雅退出:
- 启动程序后,用
kill <pid></pid>(发 SIGTERM)观察是否打印清理日志并退出 - 用
kill -INT <pid></pid>模拟 Ctrl+C,确认行为一致 - 故意注释掉信号注册,再测试——应直接退出无清理,反向证明机制有效
- 注意:kill -9 无法捕获,这是设计使然,不是 bug











