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

相关推荐
云计算练习生15 小时前
linux shell编程实战 10 Git工具详解与运维场景实战
linux·运维·git
虚伪的空想家17 小时前
KVM的ubuntu虚机如何关闭安全启动
linux·安全·ubuntu
t198751281 天前
在Ubuntu 22.04系统上安装libimobiledevice
linux·运维·ubuntu
skywalk81631 天前
linux安装Code Server 以便Comate IDE和CodeBuddy等都可以远程连上来
linux·运维·服务器·vscode·comate
晚风吹人醒.1 天前
缓存中间件Redis安装及功能演示、企业案例
linux·数据库·redis·ubuntu·缓存·中间件
Hard but lovely1 天前
linux: pthread库的使用和理解
linux
这儿有一堆花1 天前
Kali Linux:探测存活到挖掘漏洞
linux·运维·服务器
松涛和鸣1 天前
从零开始理解 C 语言函数指针与回调机制
linux·c语言·开发语言·嵌入式硬件·排序算法
皮小白1 天前
ubuntu开机检查磁盘失败进入应急模式如何修复
linux·运维·ubuntu
邂逅星河浪漫1 天前
【CentOS】虚拟机网卡IP地址修改步骤
linux·运维·centos