Linux 信号处理概览

在Linux系统中,信号(Signal)是进程间通信的关键机制,通常用于通知进程发生特定事件。例如,用户按下Ctrl+C会发送SIGINT信号来终止进程,或程序访问非法内存时触发SIGSEGV信号。信号处理是进程响应这些事件的核心机制。


一、信号分类 ⚡

Linux支持多种信号(可通过kill -l查看)。常见信号及其默认行为如下:

  • 终止进程

    • SIGTERM(优雅终止)

    • SIGKILL(强制终止,不可捕获)

    • SIGINT(Ctrl+C)

  • 核心转储

    • SIGSEGV(段错误)

    • SIGFPE(算术错误)

  • 挂起/继续

    • SIGHUP(终端断开)

    • SIGSTOP(暂停进程,不可捕获)

  • 其他

    • SIGALRM(定时器)

    • SIGCHLD(子进程状态变化)


二、信号处理函数 📜

进程可通过注册信号处理函数自定义信号行为,常用的API如下:

  1. signal() 函数 🛠️

    #include <signal.h>
    void (*signal(int signum, void (*handler)(int)))(int);

  • 功能 :为信号signum绑定处理函数handler

  • 示例

    复制代码
    void handler(int sig) {
      printf("Received SIGINT\n");
      exit(0);
    }
    signal(SIGINT, handler); // 捕获Ctrl+C
  • 缺点:行为因系统而异,缺乏灵活性(如无法控制信号屏蔽)。

  1. sigaction() 函数(推荐) 👍

    #include <signal.h>
    int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

  • 参数

    • act:指定新行为,通过struct sigaction配置:

      • sa_handler:处理函数(或SIG_IGN/SIG_DFL)。

      • sa_mask:在处理信号时阻塞其他信号。

      • sa_flags:控制行为(如SA_RESTART自动重启被中断的系统调用)。

    • oldact:保存旧的处理配置。

  • 示例

    复制代码
    struct sigaction act;
    act.sa_handler = handler;
    sigemptyset(&act.sa_mask); // 不阻塞其他信号
    act.sa_flags = 0;
    sigaction(SIGINT, &act, NULL);

三、编写信号处理函数的注意事项 ⚠️

  1. 可重入性 :避免使用非异步安全函数(如printfmalloc),可能引发竞态条件。

    • 使用volatile变量原子操作传递状态到主程序。
  2. 执行时间短:避免长时间操作,尽快返回。

  3. 避免修改全局状态:如需修改,需通过信号屏蔽等手段保证原子性。


四、信号阻塞与解除 🛑

通过信号掩码控制进程对信号的响应:

  • sigprocmask():设置/修改进程的信号掩码。

  • sigpending():检查未决(被阻塞)的信号。

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


五、实时信号(RT Signals) ⏱️

  • 范围SIGRTMINSIGRTMAX(如32-64)。

  • 特点 :支持排队(避免丢失)、可携带额外数据(通过sigqueue()发送)。

  • 使用

    复制代码
    union sigval value;
    value.sival_int = 123;
    sigqueue(pid, SIGRTMIN, value); // 发送附带数据的信号

六、发送信号的常用方法 📬

  • 命令kill -信号 PID(如kill -9 1234)。

  • 系统调用

    • kill():向指定进程发送信号。

    • raise():向自身发送信号。

    • alarm():设置定时器(触发SIGALRM)。


七、多线程中的信号处理 🔄

  • 信号处理函数是进程级:所有线程共享同一处理函数。

  • 信号掩码是线程级:各线程可独立设置阻塞的信号。

  • 建议

    • 主线程统一处理信号(通过sigwait()同步等待)。

    • 避免在多线程中使用异步信号处理。


八、典型应用场景 🏗️

  1. 优雅退出 :捕获SIGTERM释放资源。

  2. 定时任务 :通过SIGALRM触发定时操作。

  3. 错误调试 :处理SIGSEGV记录错误信息。


示例:使用sigaction处理SIGINT 📝

复制代码
#include <stdio.h>
#include <signal.h>
#include <unistd.h>

volatile sig_atomic_t flag = 0;

void handler(int sig) {
    flag = 1; // 安全设置标志位
}

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

    while (!flag) {
        sleep(1);
        printf("Running...\n");
    }
    printf("Exiting gracefully\n");
    return 0;
}

总结 🏁

Linux信号处理是控制进程行为的重要机制。通过合理使用signal()sigaction()注册处理函数,结合信号阻塞、实时信号等特性,可以实现灵活的事件响应。需要特别注意处理函数的可重入性,避免竞态条件,确保程序健壮性。

相关推荐
逆小舟1 小时前
【Linux】人事档案——用户及组管理
linux·c++
青草地溪水旁1 小时前
pthread_mutex_lock函数深度解析
linux·多线程·pthread
太空的旅行者2 小时前
告别双系统——WSL2+UBUNTU在WIN上畅游LINUX
linux·运维·ubuntu
人工智能训练师4 小时前
Ubuntu22.04如何安装新版本的Node.js和npm
linux·运维·前端·人工智能·ubuntu·npm·node.js
灿烂阳光g4 小时前
domain_auto_trans,source_domain,untrusted_app
android·linux
Ronin3056 小时前
【Linux系统】日志与策略模式
linux·策略模式·日志
ZzzK,6 小时前
JAVA虚拟机(JVM)
java·linux·jvm
Aspiresky7 小时前
浅析Linux进程信号处理机制:基本原理及应用
linux·运维·信号处理
ajassi20008 小时前
linux C 语言开发 (八) 进程基础
linux·运维·服务器
..过云雨8 小时前
05.【Linux系统编程】进程(冯诺依曼体系结构、进程概念、进程状态(注意僵尸和孤儿)、进程优先级、进程切换和调度)
linux·笔记·学习