Linux-信号1
本次,我们来学习关于信号。
前言:
信号与我们上一篇所说的信号量完全不是一个概念,不要混淆了!
关于信号部分,我们的大概流程:
1.引入信号概念(通过输出一堆结论,帮助理解)
2.介绍信号的产生
3.信号的保存
4.信号的处理
现在,我们来正式2学习它:
一、信号的概念:
信号是具有特定含义的“提示/指令”,比如生活中的信号弹、上下课铃声、红绿灯,以及系统中的发令枪、外卖电话等。
那么,谁来认识这个信号?谁来记住这个信号?
答案是:进程。
因此,进程对于信号的处理必须得具备一些能力:
信号的处理能力,属于进程内置功能等待一部分!
1. 提前具备能力:进程必须能“识别信号+处理信号”,即使没收到信号,也要提前知道各类信号的处理方式。
2. 延迟处理的合理性:收到信号后,进程可能不会立即处理,会选择“合适的时机”处理。
3. 临时存储能力:信号产生到处理之间存在“时间窗口”,进程需临时保存“信号已发生”的信息。因此!信号的处理方式有:
1.默认动作
2.忽略
3.自定义动作(信号捕捉)
你怎么认识这些信号的?有人教,然后我记住了,即:
二、对信号的基础认知
1. 识别与处理:要先“识别信号”,再掌握“信号的处理方法”(比如记住常见信号对应的行为)。
2. 预判性:即使信号还没产生,也需要提前知道“信号来之后该做什么”。
3. 延迟处理:信号产生后,可能不会立即处理(因为有更重要的事),所以有个时间窗口,在“时间窗口”内必须得记住信号已到达。eg:你买了一个商品,快递过来,你就会识别到快递到的时候该怎么取处理!(识别信号)
快递到了后,由于时间分配问题,你并不一定立即去拿(有个时间窗口),但是你会记住你有个快递需要去取(本质就是你记住了信号)。最后,你有空把快递拿回家后,你怎么处理这个快递(立即打开?放到一边忽略?把它送给别人(自定义))由你自己决定!
在这个过程中,都是异步进行的,即你并不知道信号什么时候到来!
技术的信号例子:
在Xshell中,我们启动一个程序后,然后通过按下一些组合键(eg:ctrl+c/ctrl+d/ctrl+d)就会触发对应的功能,这本质上也是由于信号的作用!
当用户按下组合键,这个键盘输入产生一个硬件中断,被OS获取,解释成信号,发送给目标前台进程前台进程因为收到信号,进而引起进程退出
ps:ctrl+c杀掉我们前台进程,Linux中,一次登录,一个终端一般会配上一个bash,每一个登录,只允许一个进程是前台进程,但是可以允许多个进程是后台进程
(这个我们在后面会讲到前台进程与后台进程!)
其实,ctrl+c本质是被进程解释成为收到了信号,2号信号。
怎么去证明它?现在我们先来介绍一个接口:
信号的自定义处理:修改特定进程对于信号的处理动作的!
#include <iostream>
#include<unistd.h>
#include<signal.h>void hander(int num)
{printf("这是%d号信号",num);
}int main()
{signal(2,hander);while(true){printf("hello Linux\n");sleep(1);}return 0;
}
当我按下ctrl+c:就会出发打印。即证明ctrl+x就是2号信号。

对于自定义的动作,是在while循环执行时触发的,并不是一调用signal就立即触发。
如果没有对应的信号产生,就不会触发。此外,信号的捕捉方法是被多个使用的,所以要把参数带上!
注意:
ctrl+c产生的信号只能发给前台进程。一个命令后面加个&可以放到后台运行,这样Shell不必等待进程
结束就可以接受新的命令,启动新的进程。
2. Shell可以同时运行一个前台进程和任意多个后台进程,只有前台进程才能接到像 Ctrl-C 这种控制键产生的信号
3. 前台进程在运行过程中用户随时可能按下 Ctrl-C 而产生一个信号,也就是说该进程的用户空间代码执行到任何地方都有可能收到 SIGINT 信号而终止,所以信号相对于进程的控制流程来说是异步
(Asynchronous)的。
现在,我们来看看操作系统中的信号有哪些?

指令:man 7 signal
1-31:普通信号
34-64:实时信号
普通信号与实时信号的区别:主要体现在:信号编号,排队机制,可靠性
对比维度 普通信号(Standard Signals) 实时信号(Real-Time Signals) 信号编号 1~31号(早期Unix继承的信号) 32~64号(Linux扩展的信号) 排队机制 不支持排队:同一信号多次触发,可能被合并为1次,仅记录“是否发生” 支持排队:同一信号多次触发会被依次记录,确保所有信号都被处理 可靠性 不可靠:信号可能丢失(如短时间内多次发送同一信号) 可靠:信号不会丢失,按发送顺序处理 默认动作 多数信号有预设默认动作(如终止、忽略) 无预设默认动作,需用户自定义处理逻辑 使用场景 用于处理通用、非紧急的事件(如终止进程) 用于处理需要精准响应的场景(如硬件事件、实时任务通知
- 普通信号的“不可靠”是历史设计导致(早期信号仅用于简单通知),而实时信号是为了解决普通信号的缺陷而扩展的。
- 部分普通信号(如 SIGKILL 、 SIGSTOP )无法被捕获/忽略,强制生效;实时信号均可被捕获、自定义处理。
键盘数据是如何知道输入给内核的,ctrl+c又是如何变成信号的?
我们现在从硬件方面上谈谈它!

键盘被摁下,肯定是OS先知道!OS怎么知道键盘上有数据呢?
我们知道,Linux下一切皆文件,键盘也是文件,又自己的内核描述符,缓冲区等等。
本质上,把键盘的数据拷贝到文件缓冲区里,所以OS知道了。
(fwrite\fread在进程中)
发送中断的过程,本质上是给某个针脚发送高低电平的过程(在CPU寄存器中0101二进制形式存放)其中1:存储了电信号,0:存储了没电信号。
为什么CPU中可保存数据呢?
本质对CPU寄存器的充放电的过程,通过键盘发送高电平,对应的针脚知道对应是几,CPU会把对应1号针脚解释成0001(32比特位,一个代表一个硬件单元),待变成高电位就有数据了。
总的来说,
CPU层面:分为硬件和软件。
硬件:键盘CPU针脚<-->CPU<--->充放电的过程<---->有无高电平
软件:解释成数据0101
转化类型,再形成计算机数据
(我们现在所学的信号,就是用软件方式,对进程模拟的硬件中断)
了解完上面的硬件方面的知识后,我们先来学习一下关于信号的产生!
信号的产生:
1.我们来具体了解一下
键盘的组合键:
- Ctrl + C :发送 SIGINT(中断信号,编号2),用于强制终止当前在终端前台运行的程序(如正在执行的脚本、命令)。
- Ctrl + Z :发送 SIGTSTP(暂停信号,编号20),将当前前台程序暂停并转入后台,后续可通过 fg 命令恢复到前台。
- Ctrl + \ :发送 SIGQUIT(退出信号,编号3),强制终止程序并生成 core dump 文件(用于程序调试,默认可能关闭,需开启核心转储)。
- Ctrl + D :不直接发送信号,而是传递 EOF(文件结束符),触发终端会话退出(如退出 bash 、 ssh 会话)或让当前读取输入的程序(如 cat )停止。
- Ctrl + Alt + Del :系统级组合键,默认触发 SIGINT/SIGTERM/SIGKILL 信号链,最终执行系统重启(具体行为可通过 /etc/inittab 或 systemd 配置修改)。
kill命令
kill -signo -pid

我们会发现,对应你杀掉不同进程,它的反应是不一样的。
并不是所有的信号都是可以被signal捕捉的,比如9,19
可以使用以下代码来证明:
int main()
{for(int i = 1; i <= 31; i++){signal(i, myhandler);}while(true){std::cout << "I am a crazy process : " << getpid() << std::endl;sleep(1);}return 0;
}
系统调用:


kill命令是调用kill函数实现的。kill函数可以给一个指定的进程发送指定的信号。raise函数可以给当前进程发送指定的信号(自己给自己发信号)。#include <signal.h>
int kill(pid_t pid, int signo);
int raise(int signo);
这两个函数都是成功返回0,错误返回-1

abort函数使当前进程接收到信号而异常终止
#include <stdlib.h>
void abort(void);
就像exit函数一样,abort函数总是会成功的,所以没有返回值
模拟野指针异常:
void myhandler(int num)
{printf("这是%d号信号\n",num);
}int main()
{// signal(11,myhandler);int*p=NULL;*p=100;while(1){sleep(2);}return 0;
}
![]()


软件条件产生信号:
异常,并不是只会由硬件产生。
讲解信号之前,我们先来认识一下:
什么是软件条件产生信号?
软件条件产生信号”是指由程序运行时的软件逻辑或状态触发的信号,而非硬件异常(如野指针触发的缺页中断)或外部操作(如键盘组合键)。这类信号是操作系统或程序自身根据预设的软件规则主动发送的。
常见的软件条件产生信号类型
1. 异常逻辑触发的信号
- 例如:程序执行 除以零运算 时,CPU会触发硬件异常,但操作系统会将其转换为 SIGFPE(浮点异常信号) ,属于“软件逻辑(非法运算)”间接触发的信号。
- 又如:进程访问了无权限的内存区域(非野指针,而是权限不足),会触发 SIGSEGV(段错误信号) 。
2. 进程间通信的信号
- 一个进程通过 kill() / killpg() 等系统调用,主动向另一个进程发送信号(如 SIGTERM 终止信号),属于软件主动发起的信号。
3. 定时器超时信号
- 程序通过 alarm() / setitimer() 设置定时器,超时后操作系统会向进程发送 SIGALRM 信号,由软件定时器条件触发。
4. 管道/IO相关信号
- 例如:进程向已关闭的管道写入数据,会触发 SIGPIPE 信号(管道破裂),由IO状态的软件条件触发。
在这里,SIGPIPE(13)信号,我们已经在管道那里验证过了:可以自行去看
https://blog.csdn.net/go_bai/article/details/154288830?spm=1001.2014.3001.5501

现在,我们来认识一下闹钟这个信号:
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号,
该信号的默认处理动作是终止当前进程。这个函数的返回值是0或者是以前设定的闹钟时间还余下的秒数

我们之前学习进程的时候看到过这样的一张图,但是当时并不理解,现在不妨再来看看!

进程退出状态(16位)被划分为两部分:
- 低7位:存储“终止信号编号”(当进程被信号杀死时),或“退出码”(当进程正常终止时)。
- 第8位:仅作为core dump标志位(1表示产生了core dump文件,0表示未产生)。
SIGINT的默认处理动作是终止进程,SIGQUIT的默认处理动作是终止进程并且Core Dump
但是,如果你用的是云服务器的话,默认的core是关闭的,为什么?
原因:(了解)
1.资源安全
2.稳定性
3.运维成本
1. 避免磁盘空间被耗尽
core 文件是进程崩溃时的内存镜像,大小通常与进程占用的内存一致(可能达数百MB甚至数GB)。若云服务器上的程序频繁崩溃,会持续生成大量 core 文件,快速占满磁盘,导致业务进程因磁盘不足而无法运行,甚至系统宕机。
2. 降低不必要的性能开销
生成 core 文件需要消耗CPU、IO等资源(将进程内存数据写入磁盘),而云服务器多为生产环境,优先保障业务性能,非调试场景下的 core 生成属于冗余开销。
3. 减少安全风险
core 文件会包含进程运行时的敏感数据(如内存中的密码、密钥、业务数据),若未妥善管理,可能泄露敏感信息;关闭默认生成可降低这类安全隐患。
4. 适配生产环境的运维逻辑
云服务器的生产环境通常依赖“进程监控+自动重启”机制(而非事后调试), core 文件主要用于开发/调试阶段,生产环境默认关闭可避免运维复杂度(无需额外清理、管理 core 文件)。
这个core有什么用呢?
它是核心转储文件,是Linux/Unix系统中进程崩溃时生成的内存镜像文件
1. 本质
当进程因严重错误(如段错误、非法内存访问)终止时,系统会将进程崩溃瞬间的内存数据、寄存器状态、程序计数器等运行时信息保存到磁盘文件中,这个文件就是core dump(简称“core”)。
2. 作用
主要用于程序调试:开发/运维人员可以通过 gdb 等调试工具加载core文件,还原进程崩溃时的状态,定位错误原因(如野指针、数组越界等)。
3. 特点
- 文件大小:通常与进程崩溃时占用的内存大小一致(可能从几十MB到数GB不等)。
- 默认状态:多数系统(含云服务器)默认关闭生成(需手动配置开启)。
- 包含内容:进程的内存数据、堆栈信息、寄存器值、程序运行上下文等,可能包含敏感信息(如密码、业务数据)。就相当于崩溃时的“现场快照“”
打开系统的core dump功能,一旦进程出现异常,OS会将进程在内存中的运行信息,给我dump(转储)到进程的当前目录(磁盘),形成core.pid文件;
运行时错误,在哪一行,直接复现问题之后,直接定位到出错行,即先运行,在core.file事后调试
想要打开:
好了,本次就分享到这里了,希望我们一起进步!
最后,到了本次鸡汤环节:
很喜欢这句话:请一定要有自信,你就是一道风景,没必要在别人风景里面仰视。






