注册SIGTERM和SIGHUP信号,通过sigaction设置处理函数,修改volatile标志位,主循环检测标志并退出,最后执行清理操作。

在 Linux 开发中,程序常常需要在接收到系统信号时进行优雅退出,比如释放资源、关闭文件描述符、保存状态等。常见的信号包括 SIGTERM(请求终止)和 SIGHUP(终端挂起或控制进程结束)。正确处理这些信号可以避免数据丢失或资源泄漏。
设置信号处理器
使用 signal() 或更推荐的 sigaction() 函数注册信号处理函数。后者更可靠,能避免某些平台上的默认行为问题。
示例:用 sigaction 捕获 SIGTERM 和 SIGHUP
#include#include #include volatile sig_atomic_t keep_running = 1;
void handle_signal(int sig) { if (sig == SIGTERM || sig == SIGHUP) { keep_running = 0; // 安全地通知主循环退出 } }
void setup_signal_handlers() { struct sigaction sa; sa.sa_handler = handle_signal; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; // 不使用 SA_RESTART,以便阻塞调用能及时中断
sigaction(SIGTERM, &sa, NULL); sigaction(SIGHUP, &sa, NULL);}
主循环中响应信号
大多数守护进程或服务程序运行在一个主循环中。通过全局标志变量判断是否继续运行。
示例:
int main() { setup_signal_handlers();while (keep_running) { // 执行业务逻辑,例如 accept、read、定时任务等 // 假设有一个非繁忙等待 for (int i = 0; i < 10 && keep_running; i++) { sleep(1); // 实际中可能是 poll/epoll 等 } printf("Working...\n"); } // 走到这一步说明收到退出信号 printf("Shutting down gracefully...\n"); // 执行清理操作 cleanup_resources(); return 0;}
保证信号安全的操作
信号处理函数中只能调用异步信号安全(async-signal-safe)的函数,如 write、_exit,不能调用 printf、malloc 等。
因此,最佳实践是仅在信号处理函数中修改 volatile sig_atomic_t 类型的标志位,把具体清理工作留给主循环完成。
常见注意事项:
- 不要在信号处理函数里打印日志或调用复杂函数
- 避免使用不可重入函数(如 strtok)
- 若主循环正在阻塞 I/O(如 read、poll),可考虑使用 self-pipe trick 或 signalfd(Linux 特有)来唤醒
- 对于多线程程序,信号最好由专门线程通过 sigwait 处理
测试信号处理
可通过命令行发送信号验证行为:
# 查看进程 PID ps aux | grep your_program发送 SIGTERM
kill -TERM
或发送 SIGHUP
kill -HUP
观察程序是否输出清理信息并正常退出。
基本上就这些。关键是注册信号、设置标志、主循环检查、执行清理。不复杂但容易忽略细节导致无法优雅退出。










