signal()要求处理函数为void(*)(int)类型,仅接受自由函数或静态成员函数;须用std::atomic等异步信号安全操作;多线程应屏蔽信号后由专用线程sigwait同步处理;推荐用sigaction替代signal以提升可靠性。

signal() 的函数指针参数必须是 void (*)(int) 类型
系统调用 signal() 要求注册的处理函数签名严格为 void handler(int),不能带额外参数,也不能是成员函数或带捕获的 lambda。这是 POSIX 标准强制规定的函数类型,任何偏差(比如 void(*)(int, void*) 或 std::function<void></void>)都会编译失败或导致未定义行为。
常见错误是试图传入类成员函数:&MyClass::onSigint —— 这会产生一个带隐式 this 参数的指针,类型不匹配。同样,std::bind 或 lambda 生成的闭包无法转换为 C 函数指针。
- 只接受自由函数或静态成员函数
- 函数体内禁止调用非异步信号安全函数(如
std::cout、malloc、printf) - 推荐仅使用
write()、siglongjmp()、atomic_flag_test_and_set()等 async-signal-safe 操作
用静态成员函数 + 全局原子变量传递上下文
若需在信号处理中访问对象状态,最轻量且可移植的方式是:将关键数据(如退出标志、计数器)声明为 static volatile sig_atomic_t 或 C++11 起的 std::atomic<bool></bool>,并在静态成员函数中读写。注意 volatile 本身不保证原子性,仅防止编译器优化;真正跨线程/信号安全需用 std::atomic(但要确保其底层 lock-free)。
class Server {
public:
static std::atomic<bool> stop_requested;
static void signal_handler(int sig) {
if (sig == SIGINT || sig == SIGTERM) {
stop_requested = true; // safe: atomic store
}
}
};
std::atomic<bool> Server::stop_requested{false};
// 注册:
signal(SIGINT, Server::signal_handler);
</bool></bool>
- 不要在 handler 中调用
Server::shutdown()等复杂方法 - 避免使用
std::string、容器、锁 —— 它们都非 async-signal-safe - 如果必须唤醒主线程,可用
sigwait()配合pthread_sigmask(),而非直接在 handler 里做业务逻辑
避免 signal(),改用 sigaction() 提升可靠性
signal() 在不同平台语义不一致(例如某些系统下自动重置 handler,某些不会),且不支持设置 SA_RESTART 或屏蔽临时信号。生产环境应始终用 sigaction() 替代。
struct sigaction sa; sa.sa_handler = Server::signal_handler; sa.sa_flags = SA_RESTART; // 阻塞系统调用被中断后自动重试 sigemptyset(&sa.sa_mask); sigaction(SIGINT, &sa, nullptr);
-
sa.sa_flags至少设SA_RESTART,否则read()、accept()可能返回EINTR - 用
sigprocmask()或pthread_sigmask()主动屏蔽信号,比依赖 handler 内部逻辑更可控 -
sigaction()是唯一能可靠指定信号掩码(sa_mask)的接口
多线程下信号处理必须显式指定接收线程
在多线程程序中,POSIX 规定信号默认递送给「任意一个未屏蔽该信号的线程」,行为不可预测。不能假设主线程一定会收到 SIGINT。正确做法是:在主线程中调用 pthread_sigmask() 屏蔽所有信号,再创建一个专用信号处理线程,用 sigwait() 同步等待。
sigset_t set; sigemptyset(&set); sigaddset(&set, SIGINT); pthread_sigmask(SIG_BLOCK, &set, nullptr); // 主线程屏蔽 // 新线程中: sigwait(&set, &sig); // 安全、同步、可中断等待 if (sig == SIGINT) Server::stop_requested = true;
- 不要在多个线程中都调用
signal()或sigaction() -
sigwait()要求信号已被所有线程屏蔽,否则可能仍随机投递 - 这种模式下,handler 函数完全不需要注册 —— 信号被转为同步事件流
std::cout 放进 handler,都可能让程序卡死或崩溃。别指望“先跑起来再修”,信号路径必须从第一行就按标准约束写。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











