温馨提示×

linux c++怎样处理信号

小樊
45
2026-09-02 03:04:50
栏目: 编程语言

在 Linux 下用 C/C++ 处理信号(signal),常见做法有以下几种,从传统方式现代推荐方式逐步说明。


一、基本概念

信号(signal)是 Linux 向进程发送的异步通知,例如:

  • SIGINT(Ctrl+C)
  • SIGTERM(终止请求)
  • SIGSEGV(段错误)
  • SIGPIPE(写已关闭的 socket)

信号处理函数必须是异步信号安全的(不能调用 printfmalloc 等)。


二、传统方式:signal()(不推荐)

#include <signal.h>
#include <iostream>

void handler(int sig) {
    std::cout << "Got signal: " << sig << std::endl;
}

int main() {
    signal(SIGINT, handler);
    while (true);
}

⚠️ 问题:

  • 行为在不同 UNIX 系统不一致
  • 信号处理完后可能恢复默认行为
  • 不够安全

三、推荐方式:sigaction()(标准、可靠)

1️⃣ 基本用法

#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_RESTART

2️⃣ 使用 SA_RESTART(自动重启系统调用)

sa.sa_flags = SA_RESTART;

适合服务器程序(避免 read/write 被信号打断)。


3️⃣ 使用 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;

可获取:

  • 发送进程 PID
  • 信号原因
  • 用户数据

四、在信号处理函数中该做什么(重要)

❌ 不要做:

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 一起用

六、常见陷阱

  1. cout / printf 在信号处理中不安全
  2. 多个线程时信号默认发给“任意线程”
  3. 忽略 SIGPIPE 很重要(网络程序)
signal(SIGPIPE, SIG_IGN);

七、总结建议

场景 推荐方式
简单程序 sigaction
服务器 sigaction + flag
epoll / 异步 signalfd
多线程 使用 pthread_sigmask

如果你愿意,我可以:

  • 给你一个 epoll + signalfd 完整示例
  • 或讲 多线程下信号处理
  • 或结合 C++17/20 写法

你现在的程序是 单线程还是服务器?

0