Linux 信号

目录

一.前置知识

1.前台进程和后台进程

a.概念理解

b.相关指令

2.信号的前置知识

[a.Linux 系统下信号的概念](#a.Linux 系统下信号的概念)

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

c.信号捕捉

3.信号的底层机制

二.详解信号

1.信号的产生

a.键盘组合键

[b.kill 指令和系统调用接口](#b.kill 指令和系统调用接口)

[① kill 指令](#① kill 指令)

[② kill() 系统调用接口](#② kill() 系统调用接口)

[③ raise() 系统调用接口](#③ raise() 系统调用接口)

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

①除零报错

②野指针报错

d.由软件条件产生信号

①匿名管道

②闹钟

2.信号的发送

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

[① Pending表(未决信号集)](#① Pending表(未决信号集))

[② Block表(信号屏蔽集)](#② Block表(信号屏蔽集))

[③ handler表(信号处理函数表)](#③ handler表(信号处理函数表))

④小结

b.信号集操作函数

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

②sigprocmask

③sigpending

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

3.信号的处理

a.用户态和内核态

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

①系统调用

②抛异常

③中断

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号信号时,执行的功能就是将自己终结。

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

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

c.信号捕捉

信号捕捉的相关系统调用接口如下:

sighandler_t signal ( int signum , sighandler_t handler );

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

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

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

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

示例:

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在接收到中断信号后,会暂停执行当前的用户态程序,转而执行与中断信号对应的内核中断处理程序,从而完成从用户态到内核态的切换。

c.重谈地址空间

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

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

相关推荐
xuanzdhc1 小时前
Linux 基础IO
linux·运维·服务器
愚润求学1 小时前
【Linux】网络基础
linux·运维·网络
bantinghy1 小时前
Linux进程单例模式运行
linux·服务器·单例模式
小和尚同志2 小时前
29.4k!使用 1Panel 来管理你的服务器吧
linux·运维
帽儿山的枪手2 小时前
为什么Linux需要3种NAT地址转换?一探究竟
linux·网络协议·安全
shadon1789 天前
回答 如何通过inode client的SSLVPN登录之后,访问需要通过域名才能打开的服务
linux
小米里的大麦9 天前
014 Linux 2.6内核进程调度队列(了解)
linux·运维·驱动开发
算法练习生9 天前
Linux文件元信息完全指南:权限、链接与时间属性
linux·运维·服务器
忘了ʷºᵇₐ9 天前
Linux系统能ping通ip但无法ping通域名的解决方法
linux·服务器·tcp/ip
浩浩测试一下9 天前
渗透测试指南(CS&&MSF):Windows 与 Linux 系统中的日志与文件痕迹清理
linux·运维·windows·安全·web安全·网络安全·系统安全