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

相关推荐
Johny_Zhao1 小时前
基于 Docker 的 LLaMA-Factory 全流程部署指南
linux·网络·网络安全·信息安全·kubernetes·云计算·containerd·yum源·系统运维·llama-factory
Wy_编程2 小时前
Linux文件相关命令
linux·运维
Viking_bird2 小时前
centos 7.5 + Hadoop 3.2.4 集群搭建
linux·运维·服务器·hadoop·centos
wdxylb3 小时前
云原生俱乐部-RH134知识点总结(1)
linux·云原生
黑客影儿3 小时前
Kali Linux 环境中的系统配置文件与用户配置文件大全
linux·运维·程序人生·安全·网络安全·系统安全·学习方法
岚天start3 小时前
Linux系统网络排查工具总结
linux·运维·网络·监控·扫描·连通性·流量
风静雪冷4 小时前
grep命令要点、详解和示例
linux
我是哈哈hh5 小时前
【MySQL】在UBuntu环境安装以及免密码登录入门
linux·数据库·mysql·ubuntu
Lovyk5 小时前
基于 Ansible 与 Jinja2 模板的 LNMP 环境及 WordPress 自动化部署实践
linux·运维·服务器·自动化·ansible
LKAI.5 小时前
传统方式部署(RuoYi-Cloud)微服务
java·linux·前端·后端·微服务·node.js·ruoyi