Linux 信号详解

目录

一.前置知识

1.前台进程和后台进程

a.概念理解

b.相关指令

2.信号的前置知识

a.Linux 系统下信号的概念

b.进程对信号的处理方式

3.信号的底层机制

二.详解信号

1.信号的产生

a.键盘组合键

b.kill 指令和系统调用接口

① kill 指令

② kill() 系统调用接口

③ raise() 系统调用接口

c.硬件级异常问题产生信号

①除零报错

②野指针报错

d.由软件条件产生信号

①匿名管道

②闹钟

2.信号的发送

a.OS给进程发信号的实质

① Pending表(未决信号集)

② Block表(信号屏蔽集)

③ handler表(信号处理函数表)

④小结

b.信号集操作函数

①有关sigset_t类型位图操作的函数接口

②sigprocmask

③sigpending

c.屏蔽进程的指定信号(代码实战)

3.信号的处理

a.用户态和内核态

b.用户态和内核态间的切换方式

①系统调用

②抛异常

③中断

c.重谈地址空间

4.信号的捕捉

a.signal

b.sigaction

c.实战演练

三.信号驱动回收子进程(附加篇)


一.前置知识

1.前台进程和后台进程

a.概念理解

前台进程:Linux系统中,前台只能运行一个进程,并且在进程运行期间,命令行失效,此时,如果我们想控制该进程,就只能通过信号!!

后台进程:可以同时运行多个进程,进程在运行期间,命令行仍起作用,但我们无法以键盘组合键的方式向后台进程发送控制指令。

由于后台进程可能同时运行多个,所以,每个后台运行的进程都会有自己的编号 [x]

b.相关指令

当我们向运行某个可执行程序时,如:./test 即让 test 可执行程序在前台运行。如果我们在其后加上 & 如: ./test & 则表示使 test 可执行程序在后台运行。

jobs —— 查看系统后台进程情况

fg + [x] —— 可以将编号为x的进程切换到前台

Ctrl + z —— 暂停前台进程,同时将该进程转到后台去

bg+ [x] —— 将后台被暂停的编号为x的进程启动

2.信号的前置知识

a.Linux 系统下信号的概念

什么是信号?简单来说,操作系统向目标进程发送的控制指令就是信号。

进程和信号的关系

①进程必须要有能力“识别”并“处理”信号,即使进程还未收到这一信号。

②进程收到某一信号时,可能不会立即对这一信号做出处理动作,也就是说,进程在收到信号和处理信号间有一个“空窗期”,这就要求进程具备对信号的临时存储能力。

kill -l  查看Linux系统中的各个信号,如下图:

man 7 signal —— 用于查看信号相关信息的命令。该命令会展示关于信号机制的详细解释,包括信号的含义、产生原因、默认行为、处理方式。

b.进程对信号的处理方式

进程对信号的处理方式一共有三种:①默认动作;  ②忽略;  ③*自定义动作(信号捕捉)。

什么是默认处理动作?--- 就是进程处理某一信号时, 该信号的功能是系统默认赋予的。例如:9号信号的功能是终止某一进程,当进程收到9号信号时,执行的功能就是将自己终结。

忽略行为该如何理解?--- 进程收到信号,但却不执行信号的功能,就是忽略。

*自定义动作指的是什么?--- 用户捕捉某个或某些信号,并对它们的功能进行修改,当进程收到这些信号时,会执行用户重新为信号赋予的功能,这就是自定义动作。

3.信号的底层机制

情景:当前台进程正在运行时,我们输入的 Ctrl + C 是如何终止进程的?或者说,我们输入 Ctrl + C后,OS底层都做了哪些事?

首先,OS如何知道外设的数据输入??—— 中断技术

当硬件设备有数据输入时,产生的光电信号经过8259表的特殊处理,会激活CPU上与该外设相连接的针脚,在CPU寄存器上生成中断号,随后,OS通过该中断号遍历中断向量表,找到表上记录的相应硬件的处理方法,即OS会从键盘缓冲区读取数据,并对读取的数据类型做判断,若其为组合的控制键(如:Ctrl + c、Ctrl + v),则OS向前台进程发送对应的信号,若为普通的输入数据,则将其拷贝到指定的内存缓冲区上。

至于,当进程收到信号后,对信号的识别、存储和执行,咱们放在后文中细讲~~

但有一点需要先提一下:信号的本质就是用软件来模拟中断的行为!!

二.详解信号

1.信号的产生

a.键盘组合键

如:

Ctrl + c   向前台进程发送2号信号 —— 其功能默认是终止进程

Ctrl + \    向前台进程发送3号信号  —— 其功能默认是终止进程

注意:9号(暂停进程)和19号(杀掉进程)不能被signal自定义捕捉!!

b.kill 指令和系统调用接口

① kill 指令

命令行上,我们可以使用 kill -x  + pid,通过目标进程的pid,向目标进程发送 x 号信号。

② kill() 系统调用接口

代码中,我们可以使用 kill(pid_t pid , int signal)函数,通过进程的 pid,向目标进程发送 signal 号信号。若发送成功,返回0;发送失败,则返回-1.

为了能够灵活的指定目标进程的控制信号,我们可以通过用命令行参数的方式拿到目标进程的pid和控制信号,如:

运行效果:

③ raise() 系统调用接口

当代码执行到 raise(int signal)函数时,OS会向自身发送 signal 号信号。

c.硬件级异常问题产生信号

①除零报错

当源文件的代码有类似除零这样的运算错误时,可执行程序运行时报错的底层原理是什么??

当可执行程序被加载到内存后,OS先将进程的 task_struct 放到运行队列,随后CPU执行程序代码,当CPU状态寄存器发现有除零操作时,其溢出标记位直接被置为1,从而形成在硬件层面上的报错,作为硬件的管理者,OS会从CPU那里拿到错误信息,又作为进程的管理者,OS把CPU溢出标记位信息翻译成终止信号,最后向目标进程发信号将其终止掉。

②野指针报错

当程序中出现这样的代码时: int * p=nullptr; *p=1;  典型的野指针问题,程序运行出错(段错误),那么其底层的原理是什么?

nullptr进程地址空间上的位置是0号地址*p=1是通过页表将1写到“与0号地址空间构成映射关系”物理内存上,但是,由于页表并未记录物理内存与地址空间上0号地址的映射关系,并且0号地址非法,所以当CPU通过MMU(内存管理单元,可以将虚拟地址翻译成物理地址)将值写入内存时,MMU报错,即硬件层面上的出错!!报错信息会被OS捕捉到,然后OS会向该进程发送终止信号,把该进程杀掉。

d.由软件条件产生信号

①匿名管道

进程间通信机制中,有个东西叫做匿名管道(博主以前文章中有过详细讲解),当匿名管道的读端关闭,写端一直向管道内写入数据的话,OS就会向该进程发送13号信号(SIGPIPE)干掉进程!

②闹钟

alarm() 函数是一个用于设置定时器的系统调用函数,它的主要功能是设置一个定时器(闹钟),当定时器时间到达时,内核会向当前进程发送14号信号(SIGALRM)信号。

总结:产生信号的方式可能有很多,但向进程发送信号的只能是OS!!

2.信号的发送

a.OS给进程发信号的实质

OS通过进程pid找到进程的PCB,然后将发送的信号写到进程PCB内的信号位图上,这就是OS给进程发送信号的本质。

那么问题来了,信号位图是什么?

我们知道,进程PCB在创建时,就已经内置了对信号的处理方法,即PCB需要具有识别、存储并执行每一种信号的能力,那么,这个能力是什么??--- 进程PCB内有关信号的三张表

进程PCB内有关信号的三张表指的是什么?

① Pending表(未决信号集)

它本身是一个位图,其中,比特位的大小表示信号值(如:位图的第8个比特位表示8号信号),比特位的内容表示进程是否收到对应信号(如:位图的第8个比特位值为0,表示进程未收到8号信号;若值为1,则表示进程收到了8号信号),Pending 表的功能是用来记录当前进程收到了哪些信号。

② Block表(信号屏蔽集)

它也是一个位图,其中,比特位的大小表示信号值,比特位的内容表示对应信号是否被进程屏蔽。

注意信号屏蔽和信号忽略的区别

信号屏蔽:信号屏蔽是指进程对特定信号进行屏蔽,使得这些信号在发生时不会被立即处理,而是处于未决状态。只有当进程的信号屏蔽集发生改变,不再屏蔽这些信号时,这些信号才会被捕获并处理。信号屏蔽是一种将信号处理进行延后的机制。

信号忽略:信号忽略则是指进程对特定信号进行忽略处理,即当这些信号发生时,进程会接收到信号,但不会对信号进行任何处理。需要注意的是,并非所有信号都可以被忽略,如SIGKILL和SIGSTOP等信号就不能被忽略。

③ handler表(信号处理函数表)

定义:handler表是一个映射表,它将信号的编号(如SIGINT、SIGTERM等)与相应的处理函数(这些处理函数可以是用户自定义的,也可以是系统默认的)建立映射关系

作用:当进程接收到一个信号时,内核会暂停当前进程的执行,并根据信号的编号在handler表中查找对应的处理函数。如果找到了处理函数,则执行该函数;如果没有找到(即该信号被忽略或未设置处理函数),则根据信号的默认行为进行处理。

④小结

实际执行信号的处理动作称为信号递达(handler).

信号从产生到递达之间的状态称为信号未决(pending).

进程可以选择阻塞某个信号(block).

被阻塞的信号产生时将保持在未决状态,暂时不递达(不处理),直到进程解除对此信号的阻塞,才执行递达的动作。

b.信号集操作函数

①有关sigset_t类型位图操作的函数接口

int  sigemptyset( sigset_t*  set);   功能:将set中所有比特位的值置为0

int  sigfillset( sigset_t*  set);  功能:将set中所有比特位的值置为1

int  sigaddset( sigset_t*  set , int signo);  功能:在set中添加信号signo

int  sigdelset( sigset_t*  set , int signo);  功能:在set中将信号signo删除

int  sigismember( sigset_t*  set , int signo);  功能:判断set中信号signo是否存在

②sigprocmask

③sigpending

int sigpending(sigset_t *set); 函数能够查询当前进程或线程的未决信号集(也就是Pending表的内容),并将该集合复制到set参数指向的信号集中。

sigpending() 函数通常与sigprocmask() 函数一起使用,以管理进程的信号屏蔽字和未决信号集合。例如,在更改进程的信号屏蔽字之前,可以使用sigpending() 函数查询当前的未决信号集合,以便在之后恢复信号屏蔽字时能够正确处理这些未决信号。

c.屏蔽进程的指定信号(代码实战)

3.信号的处理

我们已经知道:OS向进程发送信号的实质是将信号写入进程PCB中的Pending表。那么,进程是在什么时候执行信号的具体功能的呢?--- 进程从内核态返回到用户态的时候,进行信号的检测和信号的处理。

那么,什么是内核态?什么又是用户态?进程又是如何从内核态返回到用户态的呢?

a.用户态和内核态

用户态和内核态是操作系统中的两种重要状态,它们分别代表了不同的运行级别和权限范围。

用户态:用户态是用户程序运行时的状态,在这种状态下,CPU只能执行非特权指令不能直接访问内存硬件设备,也不能执行特权操作,如修改系统配置、访问其他进程的内存等。用户态下的程序运行在用户空间,其资源访问权限受到严格限制,只能访问自己的[0 , 3GB]空间,以确保系统的安全性和稳定性。

内核态:内核态是操作系统内核运行时的状态,在这种状态下,CPU可以执行所有的指令,包括特权指令,可以访问所有的内存地址和硬件设备,拥有最高的权限。内核态下的程序运行在内核空间,负责管理系统资源、处理硬件事件、提供系统服务等。

b.用户态和内核态间的切换方式

①系统调用

用户态进程通过系统调用请求操作系统提供服务时,会触发从用户态到内核态的切换,系统调用是用户态进程主动要求切换到内核态的一种方式。

②抛异常

当CPU在执行用户态下的程序时,如果发生某些事先不可知的异常(如缺页异常),会触发由当前运行进程切换到处理此异常的内核相关程序中,从而转到内核态。

③中断

当外围设备完成用户请求的操作后,会向CPU发出中断信号。CPU在接收到中断信号后,会暂停执行当前的用户态程序,转而执行与中断信号对应的内核中断处理程序,从而完成从用户态到内核态的切换。

进程执行信号功能的底层示意图

图解:操作系统在用户态执行系统调用接口时,会从用户态——>内核态,以内核态的权限执行内核代码,当OS从内核态返回到用户态时会进行信号检测,若有用户自定义捕捉信号,则执行处理方法(内核——>用户),执行完后,会通过特定的系统调用接口,从用户再回到内核态,最后从内核态携着系统调用接口的返回信息回到用户态,即主控制流程。

c.重谈地址空间

就如曾经的库函数调用一样,调用系统调用接口,也是在进程的地址空间上进行的!!

操作系统的朴素理解:就是基于一个时钟中断的死循环!!

4.信号的捕捉

 什么是信号捕捉?本质是对某个或某些信号的功能进行重定义,当OS给当前进程发送相关信号时,进程会执行重定义后的功能,这就是信号捕捉。

如何进行信号捕捉?--- 系统调用接口:①signal;   ②sigaction。

接下来,咱们会详解这两个接口的功能~~

a.signal

sighandler_t  signal ( int signum , sighandler_t handler );

signum 是我们要自定义的信号,handler 是 void ( * )( int ) 类型的回调函数,该回调函数的功能需要我们自主实现。当进程的代码执行到该系统调用接口后,此时,如果我们在向该进程发送 signum 信号的话,进程执行的就是 handler 函数中的代码~~

示例:

我们知道,当进程收到信号时,对信号的处理有三种方式:①忽略;②默认处理;③自定义捕捉

而 signal() 接口不仅可以实现信号的捕捉,它还能决定进程对信号的处理方式。当 signal() 第二个参数传递咱们自定义的函数时,signal() 执行的是自定义功能;当 signal() 第二个参数传递的是 SIG_IGN 时,表示信号忽略;当 signal() 第二个参数传递的是 SIG_IGN 时,表示执行信号的默认处理方法;

信号的忽略 —— signal( signum , SIG_IGN );

信号的默认处理 —— signal( signum , SIG_DFL );

信号的自定义捕捉 —— signal( signum , handler );

b.sigaction

int sigaction(int signum, const struct sigaction* act, struct sigaction* oact);

第一个参数 signum:指定要操作的信号编号,除了9号信号(SIGKILL)19号信号(SIGSTOP)之外,其他所有信号都可以被处理。

第二个参数 act:用于指定新的信号处理方式,与 signal 中第二个参数作用类似。

第三个参数 oact:拿到老的 act,即上一次信号的处理方法。

详解struct sigaction 结构体

struct sigaction

{  
    void (*sa_handler)(int);
    void (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t sa_mask; 
    int sa_flags; 
};

sa_handler:信号处理函数指针,与signal函数中的handler参数类似。

sa_sigaction:作用是存放上一次使用的 handler。

sa_mask:信号屏蔽集,指定在信号处理函数执行期间应被阻塞的信号。

c.实战演练

情景:当进程在执行某个信号时,再次收到该信号会如何处理?我们又该怎么用代码得到我们想要的答案?

答:当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么,它会被阻塞到当前处理结束为止。如果在调用信号处理函数时,除了当前信号被自动屏蔽外,还希望自动屏蔽另外一些信号,则用 sigset_t sa_mask 字段说明这些需要额外屏蔽的信号,当信号处理函数返回时自动恢复原来的信号屏蔽字

代码验证如下:

运行结果如下:

三.信号驱动回收子进程(附加篇)

SIGCHLD信号——基于“信号驱动”回收子进程。

在进程一章中讲过可以用 wait 和 waitpid 函数来解决僵尸进程问题. 其中,父进程可以阻塞等待子进程结束,也可以非阻塞轮询式查询子进程是否结束。若采用第一种方式,父进程阻塞了就不能执行自己的工作了;若采用第二种方式,父进程在执行自己任务的同时,还要记得时不时轮询式查询一下子进程的运行状态,程序实现较为复杂。

其实,子进程在终止时会给父进程发送 SIGCHLD 信号,该信号的默认处理方式是忽略。所以,父进程可以自定义 SIGCHLD 信号的处理函数,这样父进程只需专心处理自己的工作,不必关系子进程。当子进程终止时,会通知父进程,此时,父进程在信号处理函数中调用 wait 直接回收子进程即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1553533.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【AIGC】AI时代的数据安全:使用ChatGPT时的自查要点

博客主页: [小ᶻZ࿆] 本文专栏: AIGC | ChatGPT 文章目录 💯前言💯法律法规背景中华人民共和国保守秘密法中华人民共和国网络安全法中华人民共和国个人信息保护法遵守法律法规的重要性 💯ChatGPT的数据使用特点ChatGPT数据安全…

学校在线学习作业批改教学管理平台的设计与实现SpringBoot+VUE

目录 一、项目背景及目标 二、技术选型 三、系统功能模块设计 四、关键技术实现 五、总结 在当今社会上,随着社会的发展和进步,对于现代的学生来说网络课程已经广泛应用于学校的每个角落,而一个课程教学管理平台对于现如今的课堂是不可缺…

资源《Arduino 扩展板4-单游戏摇杆》说明。

资源链接: Arduino 扩展板4-单游戏摇杆 1.文件明细: 2.文件内容说明 包含:AD工程、原理图、PCB。 3.内容展示 4.简述 该文件为PCB工程,采用AD做的。 该文件打板后配合Arduino使用,属于Arduino的扩展板。 该文件…

华为资源分享

紫光云文档中心提供弹性计算服务文档https://www.unicloud.com/document/product/ElasticComputeService/index.html报文格式华为报文格式资料Info-Finder(在线工具) 报文格式华为IP网络电子书华为IP网络相关电子书IP网络系列丛书 - 华为企业业务华为产品…

(C语言贪吃蛇)11.贪吃蛇方向移动和刷新界面一起实现面临的问题

目录 前言 实现效果 支持方向变换 修改默认效果 如何修改 总结 前言 我们上节实现了不需要按下右键就可以是贪吃蛇自发的向右移动,本节我们主要来解决贪吃蛇方向移动和刷新界面所遇到的问题。 实现效果 上图是我们希望实现的效果,我们可以自发地控…

【递归】13. leetcode 1457. 二叉树中的伪回文路径

1 题目描述 题目链接:二叉树中的伪回文路径 2 解答思路 第一步:挖掘出相同的子问题 (关系到具体函数头的设计) 第二步:只关心具体子问题做了什么 (关系到具体函数体怎么写,是一个宏观的过…

已解决:Could not find artifact xxx

已解决:Could not find artifact xxx 文章目录 写在前面问题描述报错原因分析 解决思路解决办法1. 检查依赖声明的正确性2. 检查远程仓库配置3. 检查网络连接4. 清理本地缓存并强制更新5. 手动上传依赖到私有仓库6. 检查本地仓库是否已被损坏 总结 写在前面 在使用…

生信初学者教程(二十三):REF+SVM筛选候选标记物

文章目录 介绍加载R包导入数据准备数据机器学习特征筛选数据分割基础模型Recursive Feature Elimination特征筛选调参最终分类模型测试集验证标记基因输出结果总结介绍 采用了REF(Recursive Feature Elimination) 结合 SVM(Support Vector Machine) 的方法,对差异基因(参…

遥感影像-语义分割数据集:Landsat8云数据集详细介绍及训练样本处理流程

原始数据集详情 简介:该云数据集包括RGB三通道的高分辨率图像,在全球不同区域的分辨率15米。这些图像采集自Lansat8的五种主要土地覆盖类型,即水、植被、湿地、城市、冰雪和贫瘠土地。 KeyValue卫星类型landsat8覆盖区域未知场景水、植被、…

Llama3.2开源:Meta发布1B和3B端侧模型、11B和90B多模态模型

最近这一两周不少互联网公司都已经开始秋招提前批面试了。 不同以往的是,当前职场环境已不再是那个双向奔赴时代了。求职者在变多,HC 在变少,岗位要求还更高了。 最近,我们又陆续整理了很多大厂的面试题,帮助一些球友…

司法质量改善:巡回法庭的准自然实验(2000-2022年)(原始数据、计算代码、最终计算结果(Excel和Dta)和参考文献)

巡回法庭的设立背景 最高人民法院自2015年起分批次设立地方巡回法庭,以期改善司法质量,促进司法公正。这种改革措施为研究提供了一个独特的机会,可以通过准自然实验的方法来评估其效果。 2000-2022年司法质量改善:巡回法庭的准自…

ML 系列: (10)— ML 中的不同类型的学习

一、说明 我们之前将机器学习方法分为三类:监督学习、无监督学习和强化学习。机器学习方法可以分为不同的类型,我们将在下面讨论最重要的类型。 二、懒惰学习与急切学习 预先学习的工作原理是使用训练数据构建模型,然后使用此模型评估测试数据…

强大的JVM监控工具

介绍 在生产环境中,经常会遇到各种各样奇葩的性能问题,所以掌握最基本的JVM命令行监控工具还是很有必要的 名称主要作用jps查看正在运行的Java进程jstack打印线程快照jmap导出堆内存映像文件jstat查看jvm统计信息jinfo实时查看和修改jvm配置参数jhat用…

水域救援方案

水域救援是一项在复杂水域环境中进行的紧急救援行动,旨在保障人民生命财产安全、维护社会稳定,并促进相关产业的发展。以下是对水域救援的全面介绍: 一、定义与重要性 水域救援是指在人员在水域中生命受到严重威胁或重要场所、建筑物受到水…

前缀和(8)_矩阵区域和

个人主页:C忠实粉丝 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C忠实粉丝 原创 前缀和(8)_矩阵区域和 收录于专栏【经典算法练习】 本专栏旨在分享学习算法的一点学习笔记,欢迎大家在评论区交流讨论💌 目录 温馨提示:…

MybatisPlus代码生成器的使用

在使用MybatisPlus以后,基础的Mapper、Service、PO代码相对固定,重复编写也比较麻烦。因此MybatisPlus官方提供了代码生成器根据数据库表结构生成PO、Mapper、Service等相关代码。只不过代码生成器同样要编码使用,也很麻烦。 这里推荐大家使…

华为Nova9开启开发人员选项

默认状态下,华为Nova9的开发人员选项是隐藏的,如下图: 要开启开发人员选项,在“设置→关于手机”中找到“HarmonyOS版本”或者“软件版本”,在版本号上连续点击,每次点击“HarmonyOS版本”和“软件版本”会…

Yocto - 使用Yocto开发嵌入式Linux系统_05 认识Bitbake工具

Meeting the BitBake Tool 通过本章,我们将开始学习 Yocto 项目引擎如何在幕后工作的旅程。正如每一段旅程一样,沟通是至关重要的,因此我们需要理解 Yocto 项目工具所使用的语言,并学习如何充分利用这些工具来实现我们的目标。 Wi…

启用vnc访问Dell 服务器IDRAC 7虚拟控制台

Dell IDRAC 7 版本太老,SSL证书过期,IDRAC的Java和本地远程虚拟机控制台访问不了,怎么办? 可以启用vnc访问IDRAC 虚拟控制台

使用pytdx获取股票行情数据

使用pytdx获取股票行情数据 先展示效果股票清单股票行情 下载通达信金融终端安装插件pytdxpython代码实现同步股票列表同步股票行情 先展示效果 股票清单 总共统计深市与沪市总计5397只股票信息。 股票行情 以下是详细的实现步骤 下载通达信金融终端 此步骤的目的是要获取…