跳转到内容

C 语言信号处理

来自代码酷
Admin留言 | 贡献2025年4月29日 (二) 04:48的版本 (Page update by admin bot)

(差异) ←上一版本 | 已核准修订 (差异) | 最后版本 (差异) | 下一版本→ (差异)

C语言信号处理

介绍

信号(Signal)是操作系统用于通知进程发生某种事件的一种机制。在C语言中,信号处理允许程序捕获并响应这些事件,例如用户按下Ctrl+C(SIGINT)或进程尝试访问无效内存(SIGSEGV)。信号处理是系统编程的重要组成部分,尤其在多进程、守护进程或需要优雅退出的程序中。

信号分为两类:

  • 标准信号:如SIGINT(中断)、SIGTERM(终止请求)、SIGKILL(强制终止)等。
  • 实时信号:如SIGRTMIN到SIGRTMAX,支持排队和携带数据。

基本概念

信号的产生

信号可以由以下方式产生:

  • 用户操作(如Ctrl+C发送SIGINT)
  • 内核检测到异常(如SIGSEGV)
  • 其他进程通过`kill()`系统调用发送

信号的处理方式

1. 默认行为:如SIGINT默认终止进程。 2. 忽略信号:如`signal(SIGINT, SIG_IGN)`。 3. 自定义处理函数:通过`signal()`或`sigaction()`注册处理函数。

信号处理函数

signal() 函数

`signal()`是传统的信号处理函数,但因其可移植性问题,推荐使用`sigaction()`。

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

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

int main() {
    signal(SIGINT, handler); // 捕获SIGINT(Ctrl+C)
    while (1) {
        printf("Running...\n");
        sleep(1);
    }
    return 0;
}

输出示例:

Running...
Running...
^CReceived signal 2
Running...

sigaction() 函数

`sigaction()`提供了更精细的控制,是更现代的替代方案。

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

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

int main() {
    struct sigaction sa;
    sa.sa_handler = handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;

    sigaction(SIGINT, &sa, NULL);
    while (1) {
        printf("Running...\n");
        sleep(1);
    }
    return 0;
}

信号集与阻塞

信号可以通过`sigprocmask()`临时阻塞,防止在处理关键代码时被中断。

sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGINT);
sigprocmask(SIG_BLOCK, &set, NULL); // 阻塞SIGINT
// 关键代码
sigprocmask(SIG_UNBLOCK, &set, NULL); // 解除阻塞

实际案例

守护进程的信号处理

守护进程通常需要处理SIGHUP(重新加载配置)和SIGTERM(优雅退出)。

void handle_sighup(int sig) {
    reload_config();
}

void handle_sigterm(int sig) {
    cleanup();
    exit(0);
}

int main() {
    signal(SIGHUP, handle_sighup);
    signal(SIGTERM, handle_sigterm);
    daemonize();
    // 主循环
}

多线程信号处理

在多线程程序中,信号处理需特别注意:

  • 使用`pthread_sigmask()`控制线程的信号掩码
  • 通常由一个专用线程处理所有信号

高级主题

信号与竞态条件

信号处理函数可能中断主程序的执行,导致竞态条件。解决方法:

  • 使用`sig_atomic_t`类型声明共享变量
  • 在关键代码段阻塞信号

实时信号

实时信号(SIGRTMIN-SIGRTMAX)支持排队和携带数据:

union sigval value;
value.sival_int = 42;
sigqueue(pid, SIGRTMIN, value); // 发送带数据的信号

常见问题

问题 解决方案
信号处理函数被中断 使用`sigaction()`并设置SA_RESTART标志
信号丢失 使用实时信号或信号队列
不可靠的信号行为 避免在信号处理函数中调用非异步安全函数

总结

信号处理是C语言系统编程的核心概念,用于:

  • 处理用户中断
  • 实现进程间通信
  • 构建健壮的守护进程

正确使用信号需要理解其异步特性和潜在陷阱。对于新项目,推荐使用`sigaction()`而非`signal()`,并遵循异步安全编程原则。

graph TD A[信号产生] --> B{处理方式} B --> C[默认行为] B --> D[忽略信号] B --> E[自定义处理] E --> F[signal] E --> G[sigaction]

信号延迟=信号处理时间CPU时间片