Linux: 进程信号初识
目录
一 前言
二 信号的感性认识
三 信号处理常见方式
四 系统信号列表
五 信号的保存
六 信号的产生
1. 通过终端按键产生信号
2. 通过系统调用向进程发送信号
3. 硬件异常产生信号
4. 软件条件产生信号

一 前言
在Linux操作系统中,进程信号是一个非常重要的概念。我们在前面的文章中已经见识过了进程信号了,比如我们在进程的一章中,尝试过向进程发送9号信号来终止进程(kill -9 pid)。所以对于用户来说,我们可以通过向进程发送特定的信号使得进程完成某些指定的动作。
二 信号的感性认识
我们在看到红灯的时候就知道需要停下,看到绿灯的时候就知道可以通过,那为什么我们在接收到这种信号之后,就知道做出相应的动作呢?这是因为我们能识别出不同的灯对应的含义,所以我们一旦接收到信号,就知道该怎么做了。
如同生活中的信号,Linux中的信号也是这样的,我们在给进程发送信号的时候,这个信号一定是进程能识别的,且具有一定含义,这样进程才能做出相应的行为。所以
- 在进程收到信号前,它就有着处理信号的能力
- 无论有没有接收到信号,进程都需要有识别信号的能力
- 无论有没有接收到信号,进程都需要有着处理信号的能力,即需要该信号表示的含义或者功能。
三 信号处理常见方式
生活中,我们是如何处理信号的?举个快递的例子,快递员给你打电话说快递到了让你去取,此时一般会有以下几种处理方式:
- 立即去取
- 由于手头有事,让快递员等一下再去取
- 暂时走不开,让快递员将快递放在固定点自己找时间去取
即在快递到的时候,我们大概率是在忙着的,也就是说快递到了这件事和我们当前的状态是异步发生的。
事实上,进程信号也是一样的,我们在给进程发送信号的时候,是异步通知的。进程在收到信号的时候大概率是在忙的,所以进程此时就可以选择对信号处理方式
- 执行该信号的默认处理动作。
- 忽略此信号。
- 接收到信号,自定义处理,即用户接收到信号之后,自定义信号的处理动作
四 系统信号列表
用 kill -l 命令可以察看系统定义的信号列表
可以看到系统一共存在着64种信号,其中1~31号都是普通的进程信号,34~64都是属于实时信号。实时信号即是要 立即处理 的信号,而普通信号不需要立即处理。 这些信号在使用的时候,可以直接使用各信号前面的数字,也可以使用信号字母。事实上,这些信号都是宏,这些宏都是定义在 signum.h 的头文件中。

五 信号的保存
我们知道信号是发给进程的, 而进程要保存信号,那么应该保存在哪里?
我们知道每一个进程的创建,系统都会为它维护一个PCB,里面存储着有关于进程的各种信息,那么是不是有关于进程的信号也应该存储在里面呢,是的。其实,进程信号就是系统通过修改PCB中的数据来打到向进程发送信号的目的。在每个进程的PCB中都描述着一个记录进程信号的 位图 ,当向进程发送信号时,操作系统就会在进程信号位图的指定位置写入1,指定位置的数据变成了1,就说明已经写入了相应的信号,即进程就已经接收到了相应的信号。
发送信号的本质:修改PCB中的信号位图。
六 信号的产生
- 通过终端按键产生信号
- 通过系统调用向进程发送信号
- 硬件异常产生信号
- 软件条件产生信号
1. 通过终端按键产生信号
我们接下来写一个小测试:

运行结果

✍我们通过 Ctrl +C 使得一个进程退出。 本质上 Ctrl +C是一个组合键----->OS------->OS将
Ctrl +C 解释成2信号。相应的进程内核结构位图第二号比特为变为1,但是此时信号并不会立刻处理,我们看到的Ctrl +C 进程就立马退出了,那是我们肉眼所看,实际cpu速度很快,信号并不会立马处理。我们并没有对信号做任何处理,那么2号信号就会进行默认处理。
如何查看信号的默认动作呢? 我们可以通过 man 7 signal 手册进行查看

如何证明 Ctrl +C 就是2号信号呢,我们可以通过系统调用函数 sighal() ,当收到我们指定信号的时候,会调用我们设置的回调函数执行对应的动作。
🍊 系统调用函数 signal()


运行结果

我们要想使得进程退出,可以使用 kill 9 PID

🌾:除了快捷键 ctrl +c 之外。ctrl +\ 也可以使得进程退出

2. 通过系统调用向进程发送信号
㊀: kill

接下来我们写一个测试来进行系统调用向进程发生信号
#include <iostream>
#include <string>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <cstdio>
#include <stdlib.h>
//mysignal.cpp
static void Usage(const std::string & proc)
{std::cout<< " Usage: "<<proc<<"pid sighno"<<std::endl;
}
// ./myprocess pid signo
int main(int argc, char* argv[])
{//2.系统调用向进程发送信号if(argc != 3){Usage(argv[0]);exit(1);}pid_t pid =atoi(argv[1]);int signo =atoi(argv[2]);//将字符串转换成整型int n=kill(pid,signo);if(n !=0){perror("kill");}
}
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
//mytest.cpp
int main()
{while(true){std::cout<<"我是一个正在运行的进程:pid"<<getpid()<<std::endl;sleep(1);}
}
makefile///
.PHONY:all
all: mysignal mytestmytest:mytest.cppg++ -o $@ $^ -std=c++11
mysignal:mysignal.cppg++ -o $@ $^ -std=c++11
.PHONY:clean
clean:rm -f mysignal mytest
测试结果:

🍎:从这个测试中我们可以认识到,向目标进程发信号,不一定要通过键盘热键,还可以通过系统调用接口。上述测试可以认为我们自主实现了kill 命令。
㊁:raise()
上述的kill()接口可以给任意进程发生信号,而raise()接口只能给自己发送信号。

#include <iostream>
#include <string>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <cstdio>
#include <stdlib.h>static void Usage(const std::string & proc)
{std::cout<< " Usage: "<<proc<<"pid sighno"<<std::endl;
}
// ./myprocess pid signo
int main(int argc, char* argv[])
{int cnt=0;while(cnt<=10){printf("cnt: %d\n",cnt++);sleep(1);if(cnt>=5){raise(3);//向自己发送3号信号结束进程。}}}
测试结果:

㊂:abort()
abort()给自己发送指定信号(SIGABRT)。

#include <iostream>
#include <string>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <cstdio>
#include <stdlib.h>
static void Usage(const std::string & proc)
{std::cout<< " Usage: "<<proc<<"pid sighno"<<std::endl;
}
// ./myprocess pid signo
int main(int argc, char* argv[])
{int cnt=0;while(cnt<=10){printf("cnt: %d\n",cnt++);sleep(1);if(cnt>=5){abort();//向自己发送3号信号结束进程。}}
}

🍏: 关于信号处理行为的理解:进程收到大部分的信号,默认处理动作都是终止进程,既然大多数信号处理动作都是终止进程,为什么还有那么多的信号呢?这是因为信号的不同,代表不同的事件,但是对事件发生之后处理的动作可以一样。
3. 硬件异常产生信号
信号产生不一定非要用户显示发送,操作系统内部也可以发送信号。
int main(int argc, char* argv[])
{std::cout<<"我在运行中..........."<<std::endl;sleep(1);int a=20;a/=0;
}

接下来我们通过 kill -l 来查看对应的信号编号
再然后我们通过 man 7 signal 手册来查看SIGFPE信号对应的信息,其中Action中有 Core 和
Term ,两者都是终止进程,但是有区别,我们后续再进行说明。
我们如何证明 Floating point exception 对应的就是8信号呢,接下来我们写个测试,来捕捉除0错误时,系统发来的信号是8信号。
void catchSig(int signo)
{std::cout<< "获取到一个信号,信号编号是: "<<signo<<std::endl;
}int main(int argc, char* argv[])
{//如果没有/0错误,这个函数不会进行调用signal(SIGFPE,catchSig);std::cout<<"我在运行中..........."<<std::endl;sleep(1);int a=20;a/=0;
}
运行结果:

🍈:1.OS是如何得知给当前进程发送8号信号的? OS怎么知道我们除0错误了呢?
2.我们只进行了一次除0,为什么系统一直给我们发送8信号?

1. OS是如何得知给当前进程发送8号信号的? OS怎么知道我们除0错误了呢?
OS----->cpu运算异常--------->OS必须知道(OS是软硬件资源的管理者)----->OS向进程发送异常信号。
2.我们只进行了一次除0,为什么系统一直给我们发送8信号?
我们在当前进程进行信号捕捉的时候自定义了信号行为,即进程收到信号并不会退出,既然没有退出,那么进程就还会再被调度。CPU内部只有一份寄存器,CPU再进行运行的时候会进行多个进程切换,也就是说/0这个进程会被调度多次,所以OS就会发送无数次8信号。
4. 软件条件产生信号
例如管道问题,当管道读端关闭,写端一直写的时候,系统会发送信号终止进程,这个信号是由软件条件(读端关闭,写端一直写)触发的,与硬件无关了,所以叫软件条件产生信号

软件产生的信号通常是指在计算机系统中,操作系统或应用程序通过特定的事件、错误情况或者编程接口(API)调用而触发的一种通知机制。这些信号可以用来通知进程发生了某些特定事件,例如用户请求终止一个进程、硬件异常(如分母为 0 的除法),定时器到期等。
SIGALRM 是一个软件产生的信号,我们可以在程序内调用 alarm()(alarm闹钟的意思) 接口来设置闹钟。等到“闹钟”响的时候,系统就会向进程发送 SIGALRM 信号(编号14),此信号的默认处理方式为终止进程。
alarm()

测试:
int main(int argc, char* argv[])
{//4.软件条件alarm(1);//设置了一秒闹钟,当程序运行1秒之后,程序退出。int cnt=0;while (true){std::cout<< "cnt: "<<cnt++<<std::endl;}
}

相关文章:
Linux: 进程信号初识
目录 一 前言 二 信号的感性认识 三 信号处理常见方式 四 系统信号列表 五 信号的保存 六 信号的产生 1. 通过终端按键产生信号 2. 通过系统调用向进程发送信号 3. 硬件异常产生信号 4. 软件条件产生信号 一 前言 在Linux操作系统中,进程信号是一个非常重…...
python 项目怎么通过docker打包
python 项目怎么通过docker打包 1. 编写Dockerfile 在Python项目的根目录下创建一个名为 Dockerfile 的文件,其内容示例如下: # 使用Python基础镜像 FROM python:3.9-slim# 设置工作目录 WORKDIR /app# 将当前目录下的所有文件复制到工作目录 COPY . /app# 安装项目依赖 R…...
MySQL-- 函数(单行函数):数值函数, 字符串函数
目录 1.数值函数 2. 字符串函数 1.数值函数 ABS:绝对值 ; SIGN:数字正负,正返回1,负返回-1 , 0返回0 ; CEIL,CEILING:取数上面的数 ;FLOOR:取数下面的数 ; MOD:取余 #基本的操作 SELECT ABS(-123),ABS…...
CSS--解决float: right在空间不够时会自动往下移的问题
原文网址:CSS--解决float: right在空间不够时会自动往下移的问题-CSDN博客 简介 众所周知,float: right在空间不够时会自动往下移。那么怎样让它不要往下移呢?本文介绍解决方案。 需求 我想写一个无需列表,每个列表后边跟一个…...
深度学习 Deep Learning 第14章 自编码器
深度学习 Deep Learning 第14章 自编码器 内容概要 本章深入探讨了自编码器(Autoencoders),这是一种用于特征学习和降维的神经网络架构。自编码器通过编码器和解码器两个部分,将输入数据映射到一个内部表示(编码&…...
C++(匿名函数+继承+多态)
#include <iostream> #include <cstring> #include <cstdlib> #include <unistd.h> #include <sstream> #include <vector> #include <memory>using namespace std;// 基类 Weapon class Weapon { protected:int atk; public:Weapon…...
软考中级网络工程师第十一章网络管理
11-1考点分析 11-2网络管理基础(记忆) 网络管理体系结构 网络管理五大功能域:故障管理、配置管理、计费管理、性能管理和安全管理。 助记: “安配能计障” 故障管理:尽快发现故障,找出故障原因&#x…...
创维E900V22C/E900V22D_S905L3(B)_安卓9.0_指示灯正常_线刷固件包
创维E900V22C/E900V22D_S905L3(B)_安卓9.0_指示灯正常_线刷固件包 线刷方法:(新手参考借鉴一下) 1、准备好一根双公头USB线刷刷机线,长度30-50CM长度最佳,同时准备一台电脑; 2、电脑上安装好刷…...
“京数青算“启新篇|北方算网与海东市数据局签署合作协议
近日,青海省海东市2025年“京数青算”推介会在北京召开。海东市委常委、副市长梁荣勃,海东市数据局局长安志忠出席会议,北方算网副总经理(主持工作)喻一鸣等60余家人工智能企业的代表参会。 梁荣勃在致辞中代表海东市…...
QML输入控件: Slider的高级外观定制(音视频控制条)
目录 引言相关阅读示例1:基础样式定制要点效果 示例2:音量控制滑块要点效果 示例3:视频进度条要点效果 解决问题总结工程下载 引言 在现代用户界面设计中,滑块控件(Slider)是一个不可或缺的交互元素。它不仅能让用户直观地进行数…...
密码学基础——古典密码学
目录 一、定义 特点: 二、发展阶段 三、代换密码 1.单表代换密码 1.1恺撒密码 1.2 移位变换 1.3 仿射变换 2.多表代换密码 维吉尼亚密码 四、置换密码 栅栏密码 一、定义 古典密码学是指在现代密码学出现之前,使用较为简单的数学方法和手工…...
KingbaseES物理备份还原之备份还原
此篇续接上一篇<<KingbaseES物理备份还原之物理备份>>,上一篇写物理备份相关操作,此篇写备份还原的具体操作步骤. KingbaseES版本:V009R004C011B003 一.执行最新物理备份还原 --停止数据库服务,并创建物理备份还原测试目录 [V9R4C11B3192-168-198-198 V8]$ sys_ct…...
C++友元与动态内存
一、友元 友元是一种定义在类外部的普通函数或类,但它需要在类体内进行说明,为了与该类的成员函数加以区别,在说明时前面加以关键字friend。友元不是成员函数,但是它可以访问类中的私有成员。 类具有封装和信息隐藏的特性。…...
catch-all路由
介绍 ✅ 什么是 Catch-All 路由? Catch-All 路由 指的是:一个能匹配“任意路径”的通配型路由。 它一般会使用 路径参数 path 类型,比如: app.get("/{full_path:path}") async def fallback_handler(full_path: str):…...
jdk21新特性详解使用总结
jdk21新特性详解总结 1.StringBuilder和StringBuffer新增了一个repeat方法 /*** Java 21的StringBuilder和StringBuffer新增了一个repeat方法*/public static void repeatStr(){var sbnew StringBuilder().repeat("*",10);System.out.println(sb);}运行结果如下&…...
子网划分超AI教程:5分钟教会划分子网
友情提示:本文内容由银河易创AI(https://ai.eaigx.com)创作平台deepseek-v3模型生成,仅供参考 前言 子网划分(Subnetting)是网络工程师和IT运维人员必须掌握的基础技能,但对于初学者来说&#…...
制造业数字化转型:流程改造先行还是系统固化数据?基于以MTO和MTS的投资回报分析
1. 执行摘要 制造业正经历一场深刻的数字化转型,企业面临着先进行流程改造以优化运营,还是直接上线系统以固化数据的战略选择。本文深入分析了以销定产(MTO)和以产定销(MTS)两种主要生产模式下,…...
【实用技巧】电脑重装后的Office下载和设置
写在前面:本博客仅作记录学习之用,部分图片来自网络,如需引用请注明出处,同时如有侵犯您的权益,请联系删除! 文章目录 前言下载设置总结互动致谢参考目录导航 前言 在数字化办公时代,Windows和…...
使用Android 原生LocationManager获取经纬度
一、常用方案 1、使用LocationManager GPS和网络定位 缺点:个别设备,室内或者地下停车场获取不到gps定位,故需要和网络定位相结合使用 2、使用Google Play服务 这种方案需要Android手机中有安装谷歌服务,然后导入谷歌的第三方库: 例如:i…...
STM32开发板上生成PWM正弦波
在STM32开发板上生成正弦波通常需要结合定时器(TIM)、数模转换器(DAC)或脉宽调制(PWM)以及时钟系统的配置。以下是分步指南: 方法1:使用DAC 定时器(推荐) 步…...
量子计算与人工智能融合的未来趋势
最近研学过程中发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击链接跳转到网站人工智能及编程语言学习教程。读者们可以通过里面的文章详细了解一下人工智能及其编程等教程和学习方法。 在当今科技飞速发展…...
关于登录鉴权session、cookie和token
一、cookie是用来解决什么问题的? 假如现有业务需求:当浏览器发起一个url请求之后,在一个会话周期内,服务端需要判断这个用户是否第一次发起请求,第一次请求展示的页面跟第N次请求需要响应的页面不同的。现在我们大部分…...
206. 反转链表 92. 反转链表 II 25. K 个一组翻转链表
leetcode Hot 100系列 文章目录 一、翻转链表二、反转链表 II三、K 个一组翻转链表总结 一、翻转链表 建立pre为空,建立cur为head,开始循环:先保存cur的next的值,再将cur的next置为pre,将pre前进到cur的位置…...
实时内核稳定性 - scheduling while atomic
scheduling while atomic问题 根因:未成对使用获取cpu_id的函数[ 291.881071][ 0] [XW]: type=0x00000003 cpuid=4 time=1725877230 subj...
离线语音识别 ( 小语种国家都支持)可定制词组
1产品介绍 离线语音模组采用神经网络算法,支持语音识别、自学习等功能。运用此模组将 AI 技 术赋能产品,升级改造出语音操控的智能硬件 ( 例如风扇、台灯、空调、马桶、按摩椅、运 动相机、行车记录仪等 ) 。支持全球多种语言识别,如中文…...
网络华为HCIA+HCIP 策略路由,双点双向
目录 路由策略,策略路由 策略路由优势 策略路由分类 接口策略路由 双点双向 双点双向路由引入特点: 联系 路由回灌和环路问题 路由策略,策略路由 路由策略:是对路由条目进行控制,通过控制路由条目影响报文的转发路径,即路…...
【面试篇】JVM
文章目录 一、JVM 内存结构1. 请详细描述 JVM 的内存结构,各个区域的作用是什么?2. 堆内存是如何划分的?新生代和老年代的比例是多少?3. Eden 区和 Survivor 区的作用是什么?它们之间是如何协作的?4. 方法区…...
【TI MSPM0】ADC DAC学习
一、样例展示 通过ADC0触发单次采样,如果采样结果大于0.5倍的VDD,就点亮LED 否则熄灭LED 编译加载运行这个历程,提供一个电压到A0_2引脚上,电压范围在0-VCC之间同时观察LED1.在上电后,默认将ADC配置到正确的引脚模式,…...
Cesium系列:从入门到实践,打造属于你的3D地球应用
一、Cesium简介 CesiumJS 是一个开源的 JavaScript 库,它能够帮助开发者创建出具有卓越性能、高精度、出色视觉质量和易用性的世界级 3D 地球仪和地图。无论是在航空航天领域,用于模拟飞行路径和展示卫星数据;还是在智能城市中,用…...
笔记1——数据通信网络基础
一、概述 数据通信网络:由路由器、交换机、防火墙、无线设备以终端构成的网络 功能:实现数据互通 二、网络设备 交换机: 特点:距离终端用户最近的设备 作用:终端接入、二层交换机 广播域:交换机连接的终端构成一个广播…...
