🧩 一、信号的捕获(Signal Handling)

在用户空间,信号的捕获依靠系统调用 signal() 或更强大的 sigaction() 实现。

1️⃣ 使用 signal() 捕获信号

#include <signal.h>
#include <stdio.h>
#include <unistd.h>

void handler(int sig) {
    printf("Caught signal %d\n", sig);
}

int main() {
    signal(SIGINT, handler); // 捕获 Ctrl+C
    while (1) pause();
    return 0;
}

👉 缺点:signal() 的行为在不同 UNIX 实现中不完全一致。

2️⃣ 使用 sigaction()

推荐使用 sigaction(),它提供更强的控制能力:

#include <signal.h>
#include <stdio.h>
#include <unistd.h>

void handler(int sig) {
    printf("Handled signal %d\n", sig);
}

int main() {
    struct sigaction sa;
    sa.sa_handler = handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sigaction(SIGTERM, &sa, NULL);
    while (1) pause();
}

  • sa_mask:表示在执行信号处理函数时要额外阻塞的信号集。
  • sa_flags:可控制信号行为,如 SA_RESTART 自动重启被中断的系统调用。

🧱 二、信号阻塞与信号集(Signal Mask)

有时我们需要**暂时屏蔽(阻塞)**某些信号,防止在关键代码段中被打断。

1️⃣ 使用 sigprocmask()

sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGINT);
sigprocmask(SIG_BLOCK, &set, NULL);

该代码阻塞 SIGINT 信号,直到调用:

sigprocmask(SIG_UNBLOCK, &set, NULL);

2️⃣ 检查当前阻塞信号集

sigset_t pending;
sigpending(&pending);
if (sigismember(&pending, SIGINT))
    printf("SIGINT is pending\n");


⚙️ 三、信号的发送

除了内核自身发送信号外,进程也可主动发送信号:

函数用途
kill(pid, sig)向指定进程发送信号
raise(sig)向当前进程发送信号
pthread_kill(thread, sig)向线程发送信号(多线程专用)

示例:

kill(getpid(), SIGUSR1);


🚀 四、实时信号(Real-Time Signals)

Linux 从 POSIX.1b 标准起引入了实时信号(Real-time Signals),范围通常是:

SIGRTMIN ~ SIGRTMAX (一般为 32~64)

与普通信号的区别:

项目普通信号实时信号
是否排队否(可能丢失)是(可排队多个)
传递顺序不确定按发送顺序
附带数据可携带整型或指针数据

使用示例(通过 sigqueue() 发送带数据的信号):

union sigval val;
val.sival_int = 42;
sigqueue(pid, SIGRTMIN, val);

接收端:

void handler(int sig, siginfo_t *info, void *context) {
    printf("Got value: %d\n", info->si_value.sival_int);
}


🧭 五、总结与最佳实践

场景推荐做法
简单信号捕获使用 sigaction()
防止临界区被打断使用 sigprocmask() 阻塞
进程通信(轻量)使用 kill()
进程通信(带数据)使用 sigqueue() 与实时信号
多线程环境使用 pthread_sigmask()pthread_kill()

🔗 参考资料与延伸阅读