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()注册处理函数,结合信号阻塞、实时信号等特性,可以实现灵活的事件响应。需要特别注意处理函数的可重入性,避免竞态条件,确保程序健壮性。

相关推荐
夏日听雨眠2 小时前
LInux(逻辑地址与物理地址的区别,文件描述符,lseek函数)
linux·运维·网络
qq_542515413 小时前
Ubuntu 22.04.4 LTS安装ToDesk最新版打不开,无响应?旧版本4.7.2_277版本分享
linux·ubuntu·todesk
火车叼位3 小时前
替代 Tiny Win10 的 Linux 方案:Debian XFCE 精简桌面搭建
linux·运维
小麦嵌入式4 小时前
FPGA入门(四):时序逻辑计数器原理与 LED 闪烁实现
linux·驱动开发·stm32·嵌入式硬件·fpga开发·硬件工程·dsp开发
皮卡蛋炒饭.5 小时前
传输层协议UDP
linux·网络协议·udp
syagain_zsx5 小时前
Linux指令初识(实用篇)
linux·运维·服务器
156002548405 小时前
基于VU9P+Zynq的双FMC基带信号处理板(支持国产替换)
信号处理
嵌入式小能手5 小时前
飞凌嵌入式ElfBoard-进程间的通信之信号处理pause
信号处理
王木风5 小时前
终端里的编程副驾:DeepSeek-TUI-项目深度拆解,实测与原理分析
linux·运维·人工智能·rust·node.js
槑槑紫6 小时前
windows系统装轻量版linux开发
linux·运维·服务器