在 Linux 下用 C/C++ 处理信号(signal),常见做法有以下几种,从传统方式到现代推荐方式逐步说明。
信号(signal)是 Linux 向进程发送的异步通知,例如:
SIGINT(Ctrl+C)SIGTERM(终止请求)SIGSEGV(段错误)SIGPIPE(写已关闭的 socket)信号处理函数必须是异步信号安全的(不能调用 printf、malloc 等)。
signal()(不推荐)#include <signal.h>
#include <iostream>
void handler(int sig) {
std::cout << "Got signal: " << sig << std::endl;
}
int main() {
signal(SIGINT, handler);
while (true);
}
⚠️ 问题:
sigaction()(标准、可靠)#include <signal.h>
#include <iostream>
#include <unistd.h>
void handler(int sig) {
write(STDOUT_FILENO, "SIGINT received\n", 17);
}
int main() {
struct sigaction sa{};
sa.sa_handler = handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGINT, &sa, nullptr);
while (true) pause();
}
✅ 优点:
SA_RESTARTSA_RESTART(自动重启系统调用)sa.sa_flags = SA_RESTART;
适合服务器程序(避免 read/write 被信号打断)。
SA_SIGINFO(获取信号详情)void handler(int sig, siginfo_t* info, void* ucontext) {
write(STDOUT_FILENO, "Signal with info\n", 18);
}
sa.sa_sigaction = handler;
sa.sa_flags = SA_SIGINFO;
可获取:
❌ 不要做:
printf
cout
malloc
lock
✅ 推荐做法:
volatile sig_atomic_t 标志pipe / eventfd#include <signal.h>
#include <unistd.h>
volatile sig_atomic_t stop = 0;
void handler(int) {
stop = 1;
}
int main() {
struct sigaction sa{};
sa.sa_handler = handler;
sigaction(SIGINT, &sa, nullptr);
while (!stop) {
// 主逻辑
}
}
signalfd(最现代、最推荐)适合 事件驱动 / epoll 程序
#include <sys/signalfd.h>
#include <signal.h>
#include <unistd.h>
int main() {
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
sigprocmask(SIG_BLOCK, &mask, nullptr);
int sfd = signalfd(-1, &mask, 0);
struct signalfd_siginfo si;
read(sfd, &si, sizeof(si));
// 在这里安全处理信号
}
✅ 优点:
epoll 一起用cout / printf 在信号处理中不安全SIGPIPE 很重要(网络程序)signal(SIGPIPE, SIG_IGN);
| 场景 | 推荐方式 |
|---|---|
| 简单程序 | sigaction |
| 服务器 | sigaction + flag |
| epoll / 异步 | signalfd |
| 多线程 | 使用 pthread_sigmask |
如果你愿意,我可以:
你现在的程序是 单线程还是服务器?