Linux中程序响应Ctrl+C后“恢复现场”的关键是:用sigaction注册信号处理函数,仅在其中设置volatile sig_atomic_t标志;主循环检测该标志后执行清理(如恢复TTY、释放资源),最后调用_exit(0)。

在 Linux 中,用户强行中断(如 Ctrl+C、Ctrl+\)触发的是 SIGINT 或 SIGQUIT 信号。要让程序在收到这类信号后“恢复现场”而非直接退出,关键不是简单忽略或打印提示,而是:注册可靠捕获函数 + 保存/还原执行上下文 + 避免竞态与重入问题。
使用 sigaction 替代 signal,确保行为可预测
signal() 在不同系统上语义不一致(如自动重置 handler、不屏蔽同类信号),而 sigaction() 提供原子性设置和精细控制:
- 用 sa_flags 设置 SA_RESTART:避免系统调用被中断后返回 EINTR,使 read()/accept() 等自动重试;
- 用 sa_mask 显式屏蔽其他干扰信号(如再按 Ctrl+C 时不嵌套执行 handler);
- 不依赖 signal() 的隐式重置,防止 handler 被意外恢复为默认动作。
在 handler 中只做最小化、异步安全操作
信号处理函数运行在中断上下文中,不能调用大多数 libc 函数(如 printf、malloc、exit)。所谓“恢复现场”,实际是指:
- 设置一个 volatile sig_atomic_t 标志位(如
volatile sig_atomic_t g_sig_received = 0;),仅在此处赋值; - 主循环中定期检查该标志,主动进入清理流程(关闭文件、释放资源、保存状态等);
- 避免在 handler 内直接 close()、write() 或 longjmp —— 这些不是 async-signal-safe 的。
主逻辑中实现可中断的“软退出”机制
真正的“恢复现场”发生在用户态主流程中,而非信号 handler 内部。典型结构如下:
- 初始化阶段:调用 sigaction 注册 handler,设置标志为 0;
- 主循环中:
while (!g_sig_received) { do_work(); }; - 退出前:执行显式 cleanup(如 sync 数据、restore terminal 属性、重置信号掩码);
- 最后调用
_exit(0)(非 exit(),避免再次触发 atexit 或 stdio flush)。
终端场景需额外恢复 TTY 状态
若程序修改过终端属性(如禁用回显、原始模式),Ctrl+C 后终端可能处于异常状态。应在 cleanup 阶段调用:
-
tcgetattr(STDIN_FILENO, &orig_term);(注册时提前保存); -
tcsetattr(STDIN_FILENO, TCSANOW, &orig_term);(退出前恢复); - 注意:
tcsetattr是 async-signal-safe 的,可在 handler 中安全调用——但更推荐只设标志,由主循环调用。


















