当前位置:首页 > 技术 > 正文内容

Linux信号机制详解

访客 技术 2026年6月26日 1

一、信号基本概念

信号是进程间通信的一种异步机制,本质上是一种软中断。

使用kill -l命令可以查看系统支持的所有信号:

kill -l

每个信号都有对应的编号和宏定义名称,这些定义可在signal.h头文件中找到。编号超过34的为实时信号,本文主要讨论编号小于34的常规信号。

二、信号的生命周期

从时间维度看,信号经历三个阶段:

  1. 信号产生
  2. 信号传递
  3. 信号处理

信号的处理方式包括:

  1. 忽略信号
  2. 执行默认处理动作
  3. 自定义信号处理函数

三、信号产生方式

1. 终端按键触发

SIGINT(Ctrl+C)和SIGQUIT(Ctrl+\)是常见的终端信号。其中SIGQUIT会导致进程终止并生成核心转储文件(Core Dump)。

核心转储是进程异常终止时将内存数据保存到磁盘的机制,主要用于调试。默认情况下系统禁用此功能,可通过以下命令启用:

ulimit -c 1024

2. 系统调用发送信号

使用kill()raise()函数可以向进程发送信号:

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

int main() {
    pid_t pid = fork();
    if (pid == 0) {
        // 子进程
        while(1) {
            printf("Child process running...\n");
            sleep(1);
        }
    } else {
        // 父进程发送信号
        sleep(3);
        kill(pid, SIGTERM);
    }
    return 0;
}

abort()函数用于使进程异常终止:

#include <stdlib.h>

void test_abort() {
    printf("About to abort...\n");
    abort();
}

3. 软件条件触发

alarm()函数用于设置定时器,超时后产生SIGALRM信号:

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

void alarm_handler(int sig) {
    printf("Alarm triggered!\n");
}

int main() {
    signal(SIGALRM, alarm_handler);
    alarm(5);  // 5秒后触发
    
    pause();   // 等待信号
    return 0;
}

4. 硬件异常触发

CPU检测到硬件异常时会生成相应信号:

  • 除零操作 → SIGFPE
  • 非法内存访问 → SIGSEGV
  • 无效指令 → SIGILL

四、信号处理机制

基础信号捕获

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

void signal_handler(int sig_num) {
    printf("Received signal: %d\n", sig_num);
}

int main() {
    signal(SIGINT, signal_handler);
    
    printf("Press Ctrl+C to test signal handling\n");
    while(1) {
        sleep(1);
    }
    return 0;
}

模拟内存访问异常

#include <signal.h>

void segv_handler(int sig) {
    printf("Segmentation fault caught: signal %d\n", sig);
    exit(1);
}

int main() {
    signal(SIGSEGV, segv_handler);
    
    int *null_ptr = NULL;
    *null_ptr = 42;  // 触发段错误
    
    return 0;
}

五、信号状态管理

核心概念

  • 信号递达:实际执行信号处理动作
  • 信号未决:信号已产生但尚未递达的状态
  • 信号阻塞:进程暂时阻止某些信号的递达

内核中的信号表示

内核为每个进程维护以下信息:

  • 未决信号集:记录已产生但未处理的信号
  • 阻塞信号集:记录被阻塞的信号
  • 信号处理函数指针数组

信号集操作

使用sigset_t类型管理信号集:

#include <signal.h>

int main() {
    sigset_t set, old_set;
    
    // 初始化信号集
    sigemptyset(&set);
    
    // 添加信号到集合
    sigaddset(&set, SIGINT);
    sigaddset(&set, SIGTERM);
    
    // 设置进程信号屏蔽字
    sigprocmask(SIG_BLOCK, &set, &old_set);
    
    // 执行关键代码段
    
    // 恢复原信号屏蔽字
    sigprocmask(SIG_SETMASK, &old_set, NULL);
    
    return 0;
}

常用信号集操作函数:

  • sigemptyset():清空信号集
  • sigfillset():填充所有信号
  • sigaddset():添加指定信号
  • sigdelset():删除指定信号
  • sigismember():检查信号是否存在

信号屏蔽操作

// 获取当前未决信号集
sigset_t pending_set;
sigpending(&pending_set);

// 检查特定信号状态
if (sigismember(&pending_set, SIGINT)) {
    printf("SIGINT is pending\n");
}

六、高级信号处理

sigaction结构

#include <signal.h>

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

int main() {
    struct sigaction act, old_act;
    
    act.sa_handler = advanced_handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    
    sigaction(SIGINT, &act, &old_act);
    
    while(1) {
        pause();
    }
    return 0;
}

可重入性问题

信号处理函数应避免调用不可重入函数:

  • 内存分配函数(malloc/free)
  • 标准I/O函数
  • 任何使用全局状态的函数

volatile关键字

用于防止编译器优化信号相关的全局变量:

volatile int signal_flag = 0;

void sig_handler(int sig) {
    signal_flag = 1;
}

int main() {
    signal(SIGINT, sig_handler);
    
    while (!signal_flag) {
        // 等待信号
    }
    printf("Signal received!\n");
    return 0;
}
标签: Linuxsignal

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。