0

0

c++怎么在linux下处理信号_c++ signal函数绑定与优雅退出【详解】

冰火之心

冰火之心

发布时间:2026-01-03 17:48:21

|

370人浏览过

|

来源于php中文网

原创

应弃用 signal() 改用 sigaction():前者行为不可靠,可能重置信号、丢失信号、无法控制掩码和系统调用重启;后者可精确设置 sa_mask、SA_RESTART,并要求仅调用异步信号安全函数。

c++怎么在linux下处理信号_c++ signal函数绑定与优雅退出【详解】

signal() 函数在 Linux 下的绑定行为很不可靠

Linux 下直接用 signal() 绑定信号处理函数,基本等于“听天由命”——它在不同系统(尤其是 glibc 版本差异)下语义不一致:signal() 可能重置为默认行为、可能不阻塞同信号递归、还可能丢失信号。POSIX 明确推荐弃用它,改用 sigaction()

  • signal(SIGINT, handler) 在某些 glibc 版本中注册后,收到一次 SIGINT 就自动恢复成 SIG_DFL,第二次 Ctrl+C 直接终止进程
  • 无法控制信号掩码,处理期间若再收到同信号(如快速连按 Ctrl+C),可能被忽略或引发未定义行为
  • 不能指定是否重启被中断的系统调用(SA_RESTART),导致 read()accept() 等意外返回 EINTR

用 sigaction() 实现可重入、可屏蔽、可重启的信号处理

真正可控的方式是 sigaction(),它能精确控制信号行为。关键点:显式清空 sa_mask、设置 SA_RESTART、避免使用非异步信号安全函数(如 printfmalloc)。

  • 必须用 sigemptyset(&sa.sa_mask) 初始化信号掩码,否则行为未定义
  • 加上 SA_RESTART 标志,让被中断的系统调用自动重试,避免满屏 EINTR
  • 处理函数里只能调用异步信号安全函数(write()_exit()sigprocmask() 等),std::coutstd::string 都不安全
struct sigaction sa;
sa.sa_handler = [](int sig) {
    write(STDERR_FILENO, "Caught SIGINT\n", 16);
    _exit(1);  // 不要用 exit() — 它不是 async-signal-safe
};
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sigaction(SIGINT, &sa, nullptr);

如何实现“优雅退出”:全局标志 + 主循环检查

信号处理函数不能做复杂逻辑,真正的清理工作必须回到主线程。通用做法是用 volatile sig_atomic_t 做原子标志位,主循环定期检查并执行析构、关闭资源等操作。

  • 必须用 volatile sig_atomic_t(而非 boolint),确保读写不被编译器优化掉,且是原子访问类型
  • 不要在信号处理函数中调用 close()deletestd::thread::join() 等——它们都不在 async-signal-safe 列表里
  • 主循环应避免长时间阻塞(如无超时的 sleep()),可用 poll()epoll_wait() 带超时来轮询标志位
volatile sig_atomic_t g_exit_requested = 0;

void signal_handler(int sig) {
    g_exit_requested = 1;
}

// 注册
struct sigaction sa;
sa.sa_handler = signal_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGTERM, &sa, nullptr);
sigaction(SIGINT,  &sa, nullptr);

// 主循环
while (!g_exit_requested) {
    // do_work();
    poll(nullptr, 0, 100); // 每 100ms 检查一次
}
// 此处执行 close(), delete, join() 等安全清理

多线程环境下信号处理要格外小心

Linux 中信号是发给整个进程的,但最终由某个线程接收。默认情况下,任何线程都可能收到信号,导致行为难以预测。若想集中处理,必须显式屏蔽所有线程的信号,只在专用线程中 sigsuspend() 等待。

Quillbot
Quillbot

一款AI写作润色工具,QuillBot的人工智能改写工具将提高你的写作能力。

下载

立即学习C++免费学习笔记(深入)”;

  • 主线程创建子线程前,先用 pthread_sigmask() 屏蔽所有关心的信号
  • 开一个专用信号处理线程,用 sigsuspend()sigwait() 同步等待信号
  • 绝对不要在多个线程里分别调用 signal()sigaction() —— 行为未定义
  • std::signal()(C++11 起)在多线程中效果与 C 的 signal() 相同,同样不推荐

信号处理本身不是难题,难的是理解哪些操作真正安全、哪些只是“碰巧没崩”。很多人用 signal()exit() 跑了一年没出事,直到某次高负载下信号丢失或重入,服务静默崩溃——那才是最麻烦的。

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

314

2023.08.02

printf用法大全
printf用法大全

php中文网为大家提供printf用法大全,以及其他printf函数的相关文章、相关下载资源以及各种相关课程,供大家免费下载体验。

72

2023.06.20

fprintf和printf的区别
fprintf和printf的区别

fprintf和printf的区别在于输出的目标不同,printf输出到标准输出流,而fprintf输出到指定的文件流。根据需要选择合适的函数来进行输出操作。更多关于fprintf和printf的相关文章详情请看本专题下面的文章。php中文网欢迎大家前来学习。

277

2023.11.28

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

314

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

524

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

49

2025.08.29

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

191

2025.08.29

c++中volatile关键字的作用
c++中volatile关键字的作用

本专题整合了c++中volatile关键字的相关内容,阅读专题下面的文章了解更多详细内容。

67

2025.10.23

php代码编辑器入口汇总
php代码编辑器入口汇总

本文整理了主流PHP代码编辑器的官网入口及在线使用链接,阅读专题下面的文章了解更多详细内容。

3

2026.01.04

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PostgreSQL 教程
PostgreSQL 教程

共48课时 | 6.6万人学习

Git 教程
Git 教程

共21课时 | 2.4万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号