跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++

Linux 信号机制详解:从除零到 SIGPIPE

Linux 信号是操作系统用于进程间通信及通知事件的机制,包含产生、保存、执行三个阶段。详细讲解了信号的处理方式(默认、忽略、自定义)、系统调用接口(kill、raise、abort),以及由硬件异常产生的信号(如除零触发 SIGFPE、野指针触发 SIGSEGV)。此外还介绍了管道读写导致的 SIGPIPE、定时器信号 SIGALRM 以及核心转储(Core Dump)的概念与应用,帮助开发者理解信号底层原理及调试方法。

小熊软糖发布于 2026/2/10更新于 2026/9/1060 浏览
Linux 信号机制详解:从除零到 SIGPIPE

在操作系统编程中,我们了解了进程间通信的内容。本文将深入探讨 Linux 中的信号机制。信号是操作系统用于进程间通信的一种机制,通常用于通知进程发生了某种事件。

信号分为三部分:信号产生 -> 信号保存 -> 信号执行。

这如何理解呢?我们可以用生活中的例子类比。例如点外卖时,电话铃声响起代表信号产生(外卖到了),你暂时继续玩游戏代表信号被保存(未立即处理),游戏结束后下楼取餐代表信号执行。生活中还有许多类似例子,如上下课铃声、红绿灯等。

在开始了解信号之前,我们先思考几个问题:

  1. 我们是如何认识这些信号的?关键在于将规则记忆在系统中,这是认识信号的关键。
  2. 即使信号没有产生,一旦产生,我们知道该做什么。
  3. 信号产生后,可能无法立即处理,会推迟到合适的时候。

认识信号

站在进程的角度,进程如何认识信号?

  1. 能够识别信号。
  2. 知道该信号的处理方式。

在信号产生到处理的间隔内,必须记住信号的到来,即信号保存。

结论:

  1. 进程必须具备识别和处理信号的能力,这是进程的内置功能。操作系统需明确进程可识别哪些信号及默认动作。
  2. 收到信号后,进程可能不会立即处理,而是在合适时机处理。
  3. 进程具有临时保存信号的能力。
#include <iostream>
#include <unistd.h>
int main() {
    while (1) {
        std::cout << "this is a process" << std::endl;
        sleep(1);
    }
    return 0;
}

运行上述代码,可以通过 Ctrl+C 终止进程。这是因为 Ctrl+C 被解释为收到了 2 号信号(SIGINT)。Linux 中的信号包含编号和宏定义名称,可在 signal.h 中找到,例如 #define SIGINT 2。编号 34 以上的是实时信号,本文主要讨论编号 34 以下的普通信号。

进程收到信号后的处理方式有三种:

  1. 默认动作行为(SIG_DFL)。
  2. 忽略信号(SIG_IGN)。
  3. 自定义动作。

对于 2 号信号,默认行为是终止进程。

验证信号处理

系统调用 signal 用于设置信号处理函数。

void (*signal(int signum, void (*handler)(int)))(int);

参数说明:

  • signum:信号编号。
  • handler:信号处理函数(系统默认动作、忽略、自定义动作)。
系统默认动作
#include <iostream>
#include <unistd.h>
#include <signal.h>
int main() {
    while (1) {
        signal(SIGINT, SIG_DFL);
        std::cout << "this is a process" << std::endl;
        sleep(1);
    }
    return 0;
}

默认情况下,进程会被终止。

忽略信号
#include <iostream>
#include <unistd.h>
#include <signal.h>
int main() {
    while (1) {
        signal(SIGINT, SIG_IGN);
        std::cout << "this is a process" << std::endl;
        sleep(1);
    }
    return 0;
}

按下 Ctrl+C 后,进程忽略该信号,不终止。

自定义动作
#include <iostream>
#include <unistd.h>
#include <signal.h>
void myhandler(int signum) {
    std::cout << "这是一个 2 号信号" << std::endl;
}
int main() {
    while (1) {
        signal(SIGINT, myhandler);
        std::cout << "this is a process" << std::endl;
        sleep(1);
    }
    return 0;
}

收到 2 号信号后,执行自定义打印语句,不终止。

键盘数据与中断机制

键盘输入数据给内核的过程涉及 I/O 中断机制。当 CPU 等待用户输入时,进程阻塞,其他进程运行。用户输入后,数据放入内核缓冲区。I/O 控制器发送中断请求,内核根据中断向量表查找处理地址,将数据放入缓冲区供进程使用。

Ctrl+C 被操作系统识别为特定组合键,映射为 2 号信号。

前 31 号信号中,只有 9 号(SIGKILL)和 19 号(SIGSTOP)不可被自定义,其余均可自定义。

产生信号的系统调用

kill

kill 用于向指定进程或进程组发送信号。

#include <signal.h>
int kill(pid_t pid, int sig);

示例简易 kill 命令实现:

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <string>
void Usage(std::string str) {
    std::cout << str << " : " << "Signum Pid" << std::endl;
}
int main(int argc, char *argv[]) {
    if (argc != 3) {
        Usage(argv[0]);
        exit(1);
    }
    kill(atoi(argv[2]), atoi(argv[1]));
    return 0;
}

raise

raise 用于向当前进程自身发送信号。

#include <signal.h>
int raise(int sig);

示例:

#include <iostream>
#include <unistd.h>
#include <signal.h>
void myhandler(int signum) {
    std::cout << "这是一个" << signum << "号信号" << std::endl;
}
int main() {
    signal(2, myhandler);
    int count = 1;
    while (1) {
        std::cout << "this is a process , pid : " << getpid() << std::endl;
        if (count % 2 == 0) {
            raise(2);
        }
        count++;
        sleep(1);
    }
    return 0;
}

abort

abort() 用于使当前进程异常终止,生成 SIGABRT。

#include <stdlib.h>
void abort(void);

示例:

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
void myhandler(int signum) {
    std::cout << "这是一个" << signum << "号信号" << std::endl;
}
int main() {
    signal(SIGABRT, myhandler);
    printf("before abort\n");
    abort();
    printf("after abort\n"); // 不会执行
    return 0;
}

由异常产生信号

整数除零

整数除零会触发硬件异常,对应 SIGFPE(8 号信号)。

#include <iostream>
#include <unistd.h>
#include <signal.h>
int main() {
    int a = 10;
    std::cout << "div before" << std::endl;
    a = a / 0;
    std::cout << "div after" << std::endl;
    return 0;
}

验证代码:

#include <iostream>
#include <unistd.h>
#include <signal.h>
void myhandler(int signum) {
    std::cout << "这是一个" << signum << "号信号" << std::endl;
}
int main() {
    signal(SIGFPE, myhandler);
    int a = 10;
    std::cout << "div before" << std::endl;
    a = a / 0;
    std::cout << "div after" << std::endl;
    return 0;
}

若自定义了处理行为,程序会持续打印,因为每次调度都会检测到 PSW 寄存器中的溢出标记位为 1,再次发送信号。

野指针异常

野指针访问非法内存会触发页故障,对应 SIGSEGV(11 号信号)。

#include <iostream>
#include <unistd.h>
#include <signal.h>
int main() {
    std::cout << "point error before" << std::endl;
    int *p = nullptr;
    *p = 10;
    std::cout << "point error after" << std::endl;
    return 0;
}

验证:

#include <iostream>
#include <unistd.h>
#include <signal.h>
void myhandler(int signum) {
    std::cout << "这是一个" << signum << "号信号" << std::endl;
}
int main() {
    signal(SIGSEGV, myhandler);
    std::cout << "point error before" << std::endl;
    int *p = nullptr;
    *p = 10;
    std::cout << "point error after" << std::endl;
    return 0;
}

通过 gdb 调试程序可以定位错误位置。

SIGPIPE 信号

当写进程向管道写入数据,而读进程已关闭时,会触发 SIGPIPE 信号,导致写进程终止。

进程定时器机制

alarm 函数用于设置定时器,到达时间后发送 SIGALRM。

unsigned int alarm(unsigned int seconds);

特点:一个进程同一时间只能有一个 alarm,新的会覆盖旧的。

正确用法示例:

#include <iostream>
#include <unistd.h>
#include <signal.h>
void myhandler(int signum) {
    std::cout << "这是一个" << signum << "号信号" << std::endl;
}
int main() {
    signal(SIGALRM, myhandler);
    alarm(5);
    while (1) {
        std::cout << "wait ......." << std::endl;
        sleep(1);
    }
    return 0;
}

Core Dump

Core Dump 是进程异常终止时将内存信息转储到文件(core.pid),用于调试。

测试代码:

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
int main() {
    pid_t id = fork();
    if (id == 0) {
        while (1) {
            std::cout << "this is child process , pid : " << getpid() << std::endl;
            sleep(1);
        }
        exit(1);
    }
    int status;
    int rid = waitpid(id, &status, 0);
    if (rid == id) {
        printf("child process quit, rid : %d, exit code : %d, exit signal : %d, core dump:%d\n", rid, (status >> 8) & 0xFF, status & 0x7F, (status >> 7) & 0x1);
    }
    return 0;
}

若发送信号的行为是 Core,当前目录会出现 core.pid 文件。结合 gdb 调试工具可快速定位错误行数和内容。

以上就是对 Linux 信号机制的概述。

目录

  1. 认识信号
  2. 验证信号处理
  3. 系统默认动作
  4. 忽略信号
  5. 自定义动作
  6. 键盘数据与中断机制
  7. 产生信号的系统调用
  8. kill
  9. raise
  10. abort
  11. 由异常产生信号
  12. 整数除零
  13. 野指针异常
  14. SIGPIPE 信号
  15. 进程定时器机制
  16. Core Dump

更多推荐文章

查看全部
  • 拆解 Garry Tan 的 gstack 架构逻辑:构建 AI 工程化约束框架
  • Hunyuan-MT-7B-WEBUI 38 语种互译功能体验
  • 5 款免费 AIGC 检测工具推荐与论文降重实践指南
  • 扣子工作流详解:逻辑结构与常见节点
  • 网络安全入门:黑客技术体系与自学路径解析
  • 2026 年 3 月全球 AI 前沿动态:模型、机器人及硬件突破
  • OpenSpec 助力 AI 编程:基于需求驱动的规范化工作流
  • 大厂 AI 岗位核心技能指南:前端、后端与算法实战
  • 2026 年 3 月 18 日人工智能前沿动态与产业观察
  • 2026 无人机 AI 算法全景图:7 大场景 50+ 算法详解
  • Java 图论基础:有向图与无向图详解
  • 利用腾讯云 HAI 与 DeepSeek 快速构建个人网页
  • 基于 Trae Solo 与豆包模型构建智能点餐应用
  • 突破 LLM 上下文瓶颈:上下文内存虚拟化 CMV 的设计与实践
  • JavaAI 插件安装与 Spring Boot 项目生成实战
  • ComfyUI 快速部署指南:基于云原生环境的节点式 AI 绘图实践
  • 利用 AI 快速掌握 XP.1024 新版本特性
  • Linux 文件系统基础操作与路径管理
  • 在 OpenClaw 中构建专业 AI 角色
  • 基于无人机的多模态目标检测高多样性基准与基线

相关免费在线工具

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online