【知识讲解】 Linux信号的认识与使用


目录

前言

[Part1. 信号基础概念](#Part1. 信号基础概念)

[Part2. 信号的产生方式](#Part2. 信号的产生方式)

[Part2.1. 键盘按键](#Part2.1. 键盘按键)

[Part2.2. 硬件异常](#Part2.2. 硬件异常)

[Part2.3. 主动发送信号](#Part2.3. 主动发送信号)

[Part2.4. 软件条件触发](#Part2.4. 软件条件触发)

[Part3. 信号在内核中的保存](#Part3. 信号在内核中的保存)

[Part3.1. 信号位图](#Part3.1. 信号位图)

[Part3.2. 信号集操作函数](#Part3.2. 信号集操作函数)

[Part3.3. sigprocmask](#Part3.3. sigprocmask)

[Part4. sigaction](#Part4. sigaction)

[Part5. 信号处理的坑点](#Part5. 信号处理的坑点)

[Part6. 信号 与 System V IPC](#Part6. 信号 与 System V IPC)

[Part7. 结语](#Part7. 结语)


前言

Linux下对于信号的了解是非常重要的,接下来来跟随小编的视角来看看吧。


let's go!!!!!!!!


Part1. 信号基础概念

信号是 Linux 下异步事件通知机制,和信号量无关。

• 本质:内核发给进程的简短通知,用来告知进程发生了某个事件。

• 特点:异步。进程正常运行时,信号随时可能抵达;进程不会立刻处理,先标记信号,等到合适时机(从内核态切回用户态前)再去处理。

• 类比:中断是CPU层面的异步通知;信号是进程层面的软件中断。


信号编号

信号用整数编号标识,Linux一共62个标准信号:

• 1 ~ 31:可靠信号(传统不可靠信号),不支持排队,同一个信号多次产生可能丢失;

• 34 ~ 64:实时信号,支持排队,不会丢失。

常用信号举例:SIGINT(2),对应终端 Ctrl + C。

收到信号后的三种处理方式

1. 默认处理(Default):操作系统内置行为,大部分是终止进程;

2. 自定义处理(Catch):注册信号处理函数,收到信号后执行我们自己写的回调;

3. 忽略处理(Ignore):收到信号直接丢弃,不做任何动作。

注意:SIGKILL(9)、SIGSTOP(19) 这两个信号不能捕获、不能忽略,只能执行默认行为。


信号处理函数类型

cpp 复制代码
typedef void (*sighandler_t)(int);

• sighandler_t:信号处理函数类型,接收一个int参数(信号编号)

• signal 函数原型:

cpp 复制代码
sighandler_t signal(int signum, sighandler_t handler);

• 参数:

◦ signum:要注册的信号编号

◦ handler:处理函数,可以填 SIG_DFL(恢复默认)、SIG_IGN(忽略)或者自定义函数指针

• 返回值:返回这个信号旧的处理函数指针。


小坑:signal 函数在不同UNIX体系行为有差异,工程代码优先使用 sigaction,更稳定可控。


Part2. 信号的产生方式

一共4大类产生途径:


Part2.1. 键盘按键

◦ Ctrl + C → SIGINT 终止进程

◦ Ctrl + \ → SIGQUIT 退出并生成core转储文件

◦ Ctrl + Z → SIGTSTP 暂停进程(放到后台)


Part2.2. 硬件异常

◦ 段错误 SIGSEGV:访问非法内存、野指针、栈溢出;MMU硬件检测到虚拟地址非法,内核发出信号

◦ 除零错误 SIGFPE:除法除数为0

◦ 总线错误 SIGBUS:非法地址对齐访问


Part2.3. 主动发送信号

◦ int raise(int sig);:给自己发信号

◦ void abort(void);:内部会触发 SIGABRT,终止进程,一般用来断言失败

◦ int kill(pid_t pid, int sig);:给指定pid的进程发送信号

pid > 0:发给pid对应的进程

pid = 0:发给调用进程所在进程组的所有进程

pid = -1:权限足够时,发给除init以外所有进程


Part2.4. 软件条件触发

◦ 定时器到期 SIGALRM、管道读端关闭写端收到 SIGPIPE 等。

补充:kill 函数名字有迷惑性,不是只能发杀死进程的信号,可以发送任意信号。


Part3. 信号在内核中的保存

**Part3.**1. 信号位图

每个进程PCB(task_struct)里面维护两张位图(bitmask),用一个整数/数组的每一位代表一个信号:

• 未决信号集(pending):某一位置1 → 这个信号已经产生,但是进程还没有处理;处理完毕后清零。

• 阻塞信号集(屏蔽字,mask):某一位置1 → 屏蔽这个信号。信号来了之后不会被处理,保留在pending位图里,等到解除屏蔽之后再处理。

逻辑判断:
信号到来 → 先看mask:
mask对应bit=0(不阻塞):合适时机立刻处理,pending清0
mask对应bit=1(阻塞):信号置在pending里挂起,暂时不处理

重点:不可靠信号不排队!
同一个不可靠信号在阻塞期间多次抵达,pending位只会置1一次,多次信号会丢失;实时信号支持排队。


Part3.2. 信号集操作函数

sigset_t 是信号位图类型,用来表示一组信号,配套函数:

cpp 复制代码
int sigemptyset(sigset_t *set);    // 清空信号集,全部置0
int sigfillset(sigset_t *set);     // 填满信号集,全部置1
int sigaddset(sigset_t *set, int signum);  // 添加一个信号
int sigdelset(sigset_t *set, int signum);  // 删除一个信号
int sigismember(const sigset_t *set, int signum); // 判断信号是否在集合内

Part3.3. sigprocmask

cpp 复制代码
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

• how:三种行为

◦ SIG_BLOCK:屏蔽,把set里的信号追加到原来的屏蔽字(mask |= set)

◦ SIG_UNBLOCK:解除屏蔽,把set里的信号从mask移除

◦ SIG_SETMASK:直接把mask替换成set的值

• oldset:出参,可以拿到修改之前旧的屏蔽字,方便后续恢复。

使用场景:临界区代码,我们不希望被信号打断 → 进入临界区屏蔽信号,执行完临界区解除屏蔽。


Part4. sigaction

cpp 复制代码
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
cpp 复制代码
struct sigaction {
    void (*sa_handler)(int);        // 老式信号处理函数
    void (*sa_sigaction)(int, siginfo_t *, void *); // 扩展版本,可以拿到发送方pid等信息
    sigset_t sa_mask;               // 【重点】执行当前信号处理函数期间,自动屏蔽的信号
    int sa_flags;
    void (*sa_restorer)(void);
};

• sa_mask:当我们正在跑这个信号回调的时候,自动屏蔽sa_mask里面的信号,防止信号嵌套打断当前handler;handler执行结束自动恢复。

• SA_SIGINFO:sa_flags开启这个标记,就会使用 sa_sigaction,拿到 siginfo_t,里面可以拿到发送信号的进程pid。

信号处理函数是在进程的用户态上下文执行,不是独立线程!handler执行的时候,主流程暂停。


Part5. 信号处理的坑点

1. 信号处理函数里面,不是所有函数都能调用!
只能调用可重入(异步信号安全)函数。printf、malloc、lock 这些非可重入函数禁止在信号handler调用,极易死锁、内存损坏。

2. 不可靠信号会丢失,不要依赖它做计数;需要可靠通知优先实时信号或者管道。

3. 信号只做通知,不要在handler写复杂业务逻辑。工程经典方案:信号handler里面只写一个字节写到管道,主循环epoll监听管道读端,把业务逻辑放到主循环处理(信号转IO事件)。

4. 子进程继承父进程的信号屏蔽字、信号处理方式;但是pending未决信号集不会继承。

5. fork之后子进程里面,继承信号处理函数;exec之后,所有捕获的信号重置为默认处理方式。


Part6. 信号 与 System V IPC

通信方式 核心特点 适用场景
信号 异步、携带信息极少(只有信号编号),内核通知 异常通知、终止、暂停、定时器事件
消息队列 面向消息,带类型,支持选择性读取 进程之间传递结构化数据
信号量 计数器,用来做互斥、同步 保护临界资源,控制并发访问

一句话区分:信号是通知事件;信号量是同步互斥工具,二者完全两码事


Part7. 结语

这篇文章我们深入的知道了信号的相关知识,接下来,小编还会带来更多Linux相关的知识,敬请期待~
最后,祝大家可以:春风得意马蹄疾,一日看尽长安花!

最后的最后,要是觉得本文还可以的话,可以点点赞,关注小编一波,谢谢大家!~

相关推荐
无毁的湖光-Al1 小时前
高可用之路-观测篇-那些年遇到过的指标幻觉
linux·数据库·tcp
叼烟扛炮1 小时前
Linux 学习笔记(二十九):进程信号(上)—— 信号捕捉与异步魔法
linux·运维·笔记·学习·1024程序员节
lingran__1 小时前
Linux进程万字详解(四) | 进程地址空间深度剖析:虚拟内存、页表映射与缺页中断
linux·虚拟地址空间·虚拟内存·1024程序员节·页表·物理内存·缺页中断
wuminyu1 小时前
JVM规范学习总结-Class文件结构
java·linux·c语言·jvm·c++
m0_547486661 小时前
《Linux操作系统应用项目教程》全套PPT课件2026
linux·运维·操作系统
西索斯coding1 小时前
qwen3.8-max 和 minimax-m3 改后端仓库实测复核:补丁通过率与调用成本别急着排冠军
linux·运维·ubuntu·ai
Hello World . .2 小时前
【嵌入式学习笔记 Day 1】
linux·数据结构·笔记
叼烟扛炮2 小时前
Linux 学习笔记(三十一):线程概念、底层分页机制与初体验
linux·运维·服务器·笔记·学习·1024程序员节
星恒随风2 小时前
Linux Socket UDP编程(一):手写UDP回显服务器与客户端,从socket、bind、recvfrom到网络字典服务器完整实战
linux·服务器·网络·笔记