signal()函数通过传入信号编号(如sigint)和c风格函数指针注册处理函数,要求处理函数为全局或static,仅能调用异步信号安全函数,且跨平台行为不一致,故推荐使用更可靠的sigaction()。

signal() 函数怎么注册信号处理函数
用 signal() 注册处理函数最直接,但只适合简单场景。它接受两个参数:信号编号(如 SIGINT)和函数指针。注意该函数指针必须是 C 风格的,不能带捕获的 lambda,也不能是成员函数——否则编译报错 invalid conversion from ‘void (A::*)()’ to ‘__sighandler_t’。
常见写法:
#include <csignal>
#include <iostream>
void handle_sigint(int sig) {
std::cout
<ul>
<li>必须用全局函数或 static 函数,不能是普通成员函数</li>
<li>处理函数里只能调用异步信号安全函数(<code>write()</code>、<code>_exit()</code>、<code>siglongjmp()</code> 等),<code>std::cout</code> 和 <code>printf</code> 都不安全</li>
<li>
<code>signal()</code> 在不同系统上语义不一致(比如 Linux 默认重置 handler,BSD 保持原状),可移植性差</li>
</ul>
<h3>sigaction() 为什么比 signal() 更可靠</h3>
<p><code>sigaction()</code> 是 POSIX 标准推荐方式,能精确控制信号行为,避免 <code>signal()</code> 的模糊语义。它用 <code>struct sigaction</code> 封装所有选项,比如是否自动阻塞同类型信号、是否重启被中断的系统调用。</p>
<p>关键点:</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/gongju/2823" title="C++14"><img
src="https://img.php.cn/upload/manual/001/431/639/6ac8b33c327c4749.png" alt="C++14" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/gongju/2823" title="C++14" class="overflowclass">C++14</a>
<p class="overflowclass">C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。</p>
</div>
<a rel="nofollow" href="/xiazai/gongju/2823" title="C++14" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
<ul>
<li>设置 <code>sa_flags |= SA_RESTART</code> 可让被信号中断的 <code>read()</code>、<code>accept()</code> 自动重试,否则可能返回 -1 并设 <code>errno = EINTR</code>
</li>
<li>设置 <code>sa_mask</code> 可临时屏蔽其他信号(比如在处理 <code>SIGUSR1</code> 时不想被 <code>SIGUSR2</code> 打断)</li>
<li>必须显式初始化整个结构体(用 <code>memset(&sa, 0, sizeof(sa))</code> 或 <code>{0}</code>),否则未赋值字段是随机值,可能导致崩溃</li>
</ul>
<p>示例:</p>
<pre class="brush:php;toolbar:false;">struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = handle_sigint;
sa.sa_flags = SA_RESTART;
sigaction(SIGINT, &sa, nullptr);
如何安全地从信号处理函数通知主逻辑
信号处理函数不能安全调用大部分 C++ 标准库,也不能操作复杂对象。真正可行的做法是用 volatile sig_atomic_t 标志位 + 主循环轮询,或者用 signalfd()(Linux 特有)把信号转成文件描述符读取。
-
volatile sig_atomic_t flag = 0;是唯一被标准保证线程/信号安全的整型类型,其他类型(包括bool、int)都可能因编译器优化出问题 - 不要在 handler 里修改
std::string、调用std::vector::push_back()或锁 mutex —— 这些都不是异步信号安全的 - 如果需要更复杂的通信(比如传递数据),应改用
self-pipe trick:信号 handler 中write()到一个 pipe,主循环select()或epoll_wait()监听该 fd
常见崩溃原因和调试线索
信号处理出问题往往表现为段错误、死锁或静默失败,而不是明显报错。典型现象包括:
- 程序收到信号后立即 crash,可能是 handler 里调用了
std::cout或free()—— 查gdbbacktrace,看栈顶是不是在malloc_consolidate或__libc_signal_handler -
SIGSEGV被反复触发停不下来,说明 handler 本身又触发了 segfault(比如解引用空指针),此时需用sigprocmask()在 handler 开头屏蔽自身信号 - Ctrl+C 没反应,检查是否被子进程继承了忽略状态(
signal(SIGINT, SIG_IGN)后 fork 的子进程也会忽略),或是否被system()、popen()临时改写了 handler
真正难的不是注册 handler,而是确保 handler 内部不越界、不调用非安全函数、不依赖主程序当前状态。哪怕只改一个 volatile sig_atomic_t,也要确认编译器没把它优化掉(加 volatile 且不放在寄存器里)。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










