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.rhkb.cn/news/437794.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TCP四次挥手过程详解

TCP四次挥手全过程 有几点需要澄清: 1.首先,tcp四次挥手只有主动和被动方之分,没有客户端和服务端的概念 2.其次,发送报文段是tcp协议栈的行为,用户态调用close会陷入到内核态 3.再者,图中的情况前提是双…

leetcode-链表篇3

leetcode-61 给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。 示例 1: 输入:head [1,2,3,4,5], k 2 输出:[4,5,1,2,3]示例 2: 输入:head [0,1,2], k 4 输出&#x…

栏目二:Echart绘制动态折线图+柱状图

栏目二:Echart绘制动态折线图柱状图 配置了一个ECharts图表,该图表集成了数据区域缩放、双Y轴显示及多种图表类型(折线图、柱状图、象形柱图)。图表通过X轴数据展示,支持平滑折线展示比率数据并自动添加百分比标识&…

从原理到代码:如何通过 FGSM 生成对抗样本并进行攻击

从原理到代码:如何通过 FGSM 生成对抗样本并进行攻击 简介 在机器学习领域,深度神经网络的强大表现令人印象深刻,尤其是在图像分类等任务上。然而,随着对深度学习的深入研究,研究人员发现了神经网络的一个脆弱性&…

今日早报 每日精选15条新闻简报 每天一分钟 知晓天下事 10月3日,星期四

每天一分钟,知晓天下事! 2024年10月3日 星期四 农历九月初一 1、 应急管理部:10月华北东南部等部分地区洪涝和风雹灾害风险较高,可能有1-2个台风登陆或明显影响我国。 2、 中国海警舰艇首次进入北冰洋,与俄海警展开联…

【MySQL 07】内置函数

目录 1.日期函数 日期函数使用场景: 2.字符串函数 字符串函数使用场景: 3.数学函数 4.控制流函数 1.日期函数 函数示例: 1.在日期的基础上加日期 在该日期下,加上10天。 2.在日期的基础上减去时间 在该日期下减去2天 3.计算两…

C#知识|基于反射和接口实现抽象工厂设计模式

哈喽,你好啊,我是雷工! 01 应用场景 在项目的多数据库支持上、业务的多算法封装、以及各种变化的业务中; 02 抽象工厂组成 抽象工厂包括抽象产品(即业务接口,可以通过抽象类或抽象接口设计)…

C++和OpenGL实现3D游戏编程【连载13】——多重纹理混合详解

🔥C++和OpenGL实现3D游戏编程【目录】 1、本节要实现的内容 前面说过纹理贴图能够大幅提升游戏画面质量,但纹理贴图是没有叠加的。在一些游戏场景中,要求将非常不同的多个纹理(如泥泞的褐色地面、绿草植密布的地面、碎石遍布的地面)叠加(混合)起来显示,实现纹理间能…

基于微信的乐室预约小程序+ssm(lw+演示+源码+运行)

摘 要 随着社会的发展,社会的方方面面都在利用信息化时代的优势。互联网的优势和普及使得各种系统的开发成为必需。 本文以实际运用为开发背景,运用软件工程原理和开发方法,它主要是采用java语言技术和mysql数据库来完成对系统的设计。整个…

信息安全工程师(24)网络安全体系建设原则与安全策略

一、网络安全体系建设原则 网络空间主权原则:维护网络空间主权是网络安全的首要原则。这要求国家在网络空间的管理、运营、建设和使用等方面具有完全自主的权利和地位,不受任何外部势力的干涉和侵犯。网络安全与信息化发展并重原则:网络安全与…

影视cms泛目录用什么程序?苹果cms二次开发泛目录插件

影视CMS泛目录一般使用的程序有很多种,(maccmscn)以下是其中几种常见的程序: WordPress:WordPress是一个非常流行的开源内容管理系统,可以通过安装一些插件来实现影视CMS泛目录功能。其中,一款常…

Win10之解决:设置静态IP后,为什么自动获取动态IP问题(七十八)

简介: CSDN博客专家、《Android系统多媒体进阶实战》一书作者 新书发布:《Android系统多媒体进阶实战》🚀 优质专栏: Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏: 多媒体系统工程师系列【…

vulnhub-Replay 1靶机

vulnhub:https://www.vulnhub.com/entry/replay-1,278/ 导入靶机,放在kali同网段,扫描 靶机在192.168.81.8,扫描端口 开启了三个端口,存在网站服务,访问 网页上有个超链接,点击后下载了这样一个…

Python开发环境配置(mac M2)

1. 前言 作为一名程序员,工作中需要使用Python进行编程,甚至因为项目需要还得是不同版本的Python如何手动管理多个版本的Python,如何给Pycharm(IDE)配置对应的interpreter等,都成为一个 “不熟练工” 的难…

Docker版MKVtoolnix的安装及中文显示

本文是应网友 kkkhi 要求折腾的,只研究了 MKVtoolnix 的安装及中文显示,未涉及到软件的使用; 什么是 MKVtoolnix ? MKVToolnix 是一款功能强大的多媒体处理工具,用于在 Linux、其他 Unix 系统和 Windows 上创建、修改和…

Ks渲染做汽车动画吗?汽车本地渲染与云渲染成本分析

Keyshot是一款强大的实时光线追踪和全域光渲染软件,它确实可以用于制作汽车动画,包括汽车模型的渲染和动画展示。Keyshot的动画功能允许用户创建相机移动、物体变化等动态效果,非常适合用于汽车动画的制作。 至于汽车动画的渲染成本&#xff…

【设计模式-命令】

定义 命令模式(Command Pattern)是一种行为设计模式,它将请求封装为一个对象,从而使您能够使用不同的请求、排队请求或记录请求,并支持可撤销的操作。该模式通过将请求与其执行分离,使得请求者和接收者之间…

OpenCV视频I/O(14)创建和写入视频文件的类:VideoWriter介绍

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 视频写入器类。 该类提供了用于写入视频文件或图像序列的 C API。 cv::VideoWriter 类是 OpenCV 库中用于创建和写入视频文件的一个类。它提供了…

云服务器部署k8s需要什么配置?

云服务器部署k8s需要什么配置?云服务器部署K8s需要至少2核CPU、4GB内存、50GBSSD存储的主节点用于管理集群,工作节点建议至少2核CPU、2GB内存、20GBSSD。还需安装Docker,选择兼容的Kubernetes版本,配置网络插件,以及确…

数据结构之链表(1),单链表

目录 前言 一、什么是链表 二、链表的分类 三、单链表 四、单链表的实现 五、SList.c文件完整代码 六、使用演示 总结 前言 本文讲述了什么是链表,以及实现了完整的单链表。 ❤️感谢支持,点赞关注不迷路❤️ 一、什么是链表 1.概念 概念:链…