Linux入门——10 信号

admin2024-08-23  10

1.信号

1.信号------信号量(两者没有任何关系)

2.信号讲什么----->整个信号的生命周期

信号的产生-----信号的保存------信号的处理

之前的kill命令,用的就是信号。

kill -l查看系统支持的信号

Linux入门——10 信号,第1张

名字本身就是宏,其实就是编号,我们在使用的时候,既可以使用名字也可以使用编号。

可以发现只有1~31和34~64个信号。

生活中的信号:发令枪,闹铃,红路灯,消息提醒,烽火台狼烟。

  • 人是可以识别红绿灯的,什么是识别,1,认识,2产生匹配的行为
  • 为什么可以识别红绿灯呢?有人教你----通过教你,让你的大脑记住了对应红绿灯属性的行为。
  • 是不是绿灯一亮,你就立刻过马路呢,有没有还有车在路中间,让你没有立刻过马路---》当信号到来的时候,我们不一定立刻处理这个信号,因为信号可能随时产生,但你可能在忙自己的事情(更重要事情),信号来了,不是立即处理的。这就是异步,你在打游戏,这时候外卖小哥给你送外卖。

同步:老师 让我取快递,这时候上课了,老师说等我来了,再上课。

  • 我们不一定立刻处理这个信号,当信号到来的时候,到信号被处理,这个中间会有一段时间----->时间窗口。在这期间我必须记住这个信号。
  • 处理信号的动作,默认动作(绿灯过马路),自定义动作(红灯亮你跳舞等),忽略动作(绿灯我继续等,不过马路)

信号是给进程发的

2.进程是如何识别信号

进程本身是程序员编写的属性和逻辑的集合-----程序员编码完成的

进程收到信号的时候,进程可能正在执行更中要的代码,所以信号不一定会被立即处理

进程本身必须要有对信号保存的能力

进程处理信号的时候,一般有三种动作(默认,自定义,忽略){信号被处理被称为信号被捕获}

2.1信号被进程保存到哪?如何保存?

保存在task_struct中

保存是否收到了信号[1~31]

struct task_struct

{

        .....

        unsigned int signal;

}

发送信号的本质,就是修改task_struct(PCB)中的信号位图。

PCB的管理系统是内核的数据结构,

PCB的管理者OS有权利修改里面的内容,所以无论我们学习多少种发送信号的方式,本质都是通过OS向目标进程发送信号!!-----》OS必须要提供发送信号的系统调用接口。

kill命令底层一定调用了底层系统接口

3.信号的产生

ctrl +c热键-----本质就是一个组合键---》OS将ctrl+c解释为2号信号(SIGINT)

使用man 7 signal详细查看2号信号的具体工作

Linux入门——10 信号,第2张

3.1signal更改产生信号后的回调函数

Linux入门——10 信号,第3张

3.2sigaction()

Linux入门——10 信号,第4张

4.信号的产生方式

4.1键盘产生信号

4.2系统调用

OS有发信号的能力,有能力不代表有使用他的能力,就比如你有写代码的能力,但你的老板在使用你的这种能力

4.2.1KILL命令

4.2.2kill()函数

Linux入门——10 信号,第5张

4.2.3raise发送信号给调用者

Linux入门——10 信号,第6张

4.2.4 abort函数(C语言提供的终止进程方式)

Linux入门——10 信号,第7张

4.3硬件异常产生信号

4.4软件条件产生信号

4.4.1alarm定时器(定时终止进程)

4.4.2ualarm ()循环发送

4.4.3setitimer()定时发送信号

4.4.4pause()进程一直阻塞,直到被信号中断

4.4.5sigsuspend()

5.关于信号处理的行为的理解

6.进程退出时的核心转储问题

当使用man 7 signal时,会出现Term和Core两种终止进程的行为

Linux入门——10 信号,第8张

Linux入门——10 信号,第9张

在a[100],a[1000]并没有发生报错。越界并不一定会使编译器报错,当在栈上创建变量的时候,栈开多大空间,你不知道,你只是使用了你所开的空间。给你分配的空间可能会比较大,所以越界,也可能在有效栈区内,不会报错。除非你访问不是你的空间。即你可能在不知情的情况修改一些你的数据。

Term代表正常结束,OS不做其他操作

Core代表不仅结束进程,OS还做其他操作

7.在OS内禁止对9号信号做捕捉

9号信号为管理员信号

8.信号的保存

8.1进程阻塞信号

8.2内核中信号的保存

  • 进程收到信号,不会被立即处理,所以要保存,
  • 进程采用位图结构来保存收到的信号

8.2.1pending表(未决表)本质是位图

当信号被置于pending位图中,就说明该信号处于未决状态

8.2.2block表(阻塞表)专业称为信号屏蔽字

当信号被置于block位图中,就说明该信号处于阻塞状态

信号在OS内的被处理

Linux入门——10 信号,第10张

 8.2.3(信号捕捉方法)函数指针&函数指针数组

Linux入门——10 信号,第11张

Linux入门——10 信号,第12张

所以我们可以得到signal(signo,handler)函数的本质就是:拿着信号编号signo,到指定的数组中找。将handler对应方法的地址填入表中。

后面当信号产生的时候,修改上面的pending表中的值,根据block表查看是否阻塞,如果没有阻塞,就进行处理。OS根据信号位置找到编号,根据编号找到对应到函数指针数组中函数的地址,调用方法去处理该信号。

8.2.4结论

  1. 如果一个信号没有产生,不妨碍它可以被阻塞
  2. 进程之所以识别信号,是OS已经设置好上述三种技术,可以识别并处理信号
  3. Linux系统当同一信号被传过来多次,只能被保存一次,相当于其他信号进行了丢失、这是针对普通信号。对于实时信号,OS会产生消息队列,来处理这些信号。

9.信号的捕捉

9.1.1什么是内核态 &用户态

  1. 我们写的代码都是在用户态的,在用户态的时候,我们可能会访问两种资源,1是操作系统自身的资源,2是硬件资源。无论是那种资源都是在OS之下的,我们要通过OS提供的接口。通过这些接口,我们称为系统调用。
  2. (你毕业称为教师(内核态),到你的毕业小学当老师,曾经一些你小学时候(用户态)进不了的地方,现在可以进去了,你依旧是你,但身份发生了变化,所以权限级别发生了变化)实际执行系统调用的“人”,是你的进程,身份是内核。
  3. 往往系统调用比较费时间一些,尽量;尽量避免频繁调用系统调用。

9.1.2我怎么知道我是用户态,还是内核态?

  • 进程在实际执行时,一定会把自己的上下文信息投递到CPU之中,CPU中存在大量寄存器,我们可以将寄存器划分为两类,1,可见寄存器,2、不可见寄存器
  • 凡是和当前进程强相关的,都称为上下文数据。寄存器只有一套,但寄存器中的值可能有多套,当进程切换的时候,他可以把上下文数据带走,回来再拿回来。
  • CPU中有一个寄存器可以直接指向当前运行进程的pcb,这就是知道那个进程在运行的原因
  • 还有的寄存器保存当前进程对应的页表起始地址,还有MMU单元,通过页表找到对应的内存地址。
  • CR3寄存器:里面有比特位,表征当前进程的运行级别,

9.1.3理解进程怎么跑到内核OS中调用方法

Linux入门——10 信号,第13张

每个进程都有自己独立的用户级页表,除此之外,OS内部还维护了一张内核级别页表,它是为了映射从虚拟到物理内存之间的OS的代码。在开机的时候,OS代码也会加载到内存,但是只有一份,所以内核级页表只有一份就够了。也可以理解为CPU有一个寄存器,对应着OS的内核级页表,进程切换的时候,该寄存器不变。

每一个进程都要有自己的地址空间(是独占的),内核空间(被影射到每一个进程的内核空间,占3~4G),所以要访问OS的接口,其实只需要在自己的地址空间上跳转就 可以了。本质就是,跳转到内核空间找到对应的地址,通过内核页表,找到内存中OS的代码,然后再返回到用户空间,进行继续执行。

每个进程都共享一个内核级页表,无论进程如何切换,都不会更改任何3~4G的内核空间。

9.1.4用户执行访问内核的接口或者数据

只要要跳转的时候,更改一下CPU中的CR3寄存器的运行级别就可以了。

系统调用的接口,起始位置会帮你把CR3的值由3(用户态)改为0(内核态)

在Linux有一个终端编号,汇编指令int 80-----陷入内核,修改为内核态

9.1.5信号被处理的时间

信号产生的时候,不会被立即处理,而是在合适的时候,那么这个合适的时候是什么时候呢?

从内核态到信号态的时候,会被处理。-----》曾经我一定进入了内核态!

什么时候进入过内核态呢?

  1. 系统调用
  2. 进程切换:进程切换的时候,没被执行完,这个进程一定会被放到运行队列中,放进去,一定要放到内核态中,以OS的身份进行执行,把进程唤醒的时候,要通过内核态把进程放到运行对列中

9.2信号捕捉

进程由用户态到内核态,好不容易来一次,肯定要干点事情,于是就找到,task_struct中的信号位图,开始按位处理信号,那么,我们能不能以内核态的身份,执行用户的代码呢?

答案是并不能,因为OS不相信任何人。所以当处理信号的时候,会通过特定的方法,将自己的身份重新更改为用户态在执行,执行完,通过特定的系统调用,跳转到内核,将所有信号处理结束,再跳转到用户态。

Linux入门——10 信号,第14张

ABCD代表四次,用户切换。如果是默认或者忽略的时候,就走到信号的检查过程,就停止了,不再往后走了。

9.3sigset_t数据类型,专门为信号设置的数据类型

每个信号只有一个bit的未决标志,非0即1,不记录该信号产生了多少次,阻塞标志也是这样表示的。因此,未决和阻塞标志可以用相同的数据类型sigset_t来存储,sigset_t称为信号集,这个类型可以表示每个信号的“有效”或“无效”状态,在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有效”和“无效”的含义是该信号是否处于未决状态。下一节将详细介绍信号集的各种操作。 阻塞信号集也叫做当前进程的信号屏蔽字(Signal Mask),这里的“屏蔽”应该理解为阻塞而不是忽略。

9.4信号集操作函数

9.4.1常用函数

#include <signal.h>
sigset_t set;//自定义信号集64bit  128bit  
int sigemptyset(sigset_t *set);//清空,全设0
int sigfillset(sigset_t *set);//全设 1
int sigaddset (sigset_t *set, int signo);//添加一个信号
int sigdelset(sigset_t *set, int signo);//删一个信号
int sigismember(const sigset_t *set, int signo);//判断信号在吗

函数sigemptyset初始化set所指向的信号集,使其中所有信号的对应bit清零,表示该信号集不包含 任何有效信号。

函数sigfillset初始化set所指向的信号集,使其中所有信号的对应bit置位,表示 该信号集的有效信号包括系统支持的所有信号。

注意,在使用sigset_ t类型的变量之前,一定要调 用sigemptyset或sigfillset做初始化,使信号集处于确定的状态。初始化sigset_t变量之后就可以在调用sigaddset和sigdelset在该信号集中添加或删除某种有效信比特科技号。

这四个函数都是成功返回0,出错返回-1。sigismember是一个布尔函数,用于判断一个信号集的有效信号中是否包含某种 信号,若包含则返回1,不包含则返回0,出错返回-1。

9.4.2sigprocmask(更改进程的block表,信号屏蔽字)

9.4.3 sigpending(读取当前进程的未决(pending)信号集)

10.捕捉信号的方法

10.1signal更改产生信号后的回调函数

10.2sigaction()

这两个函数在第3.1和3.2。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明原文出处。如若内容造成侵权/违法违规/事实不符,请联系SD编程学习网:675289112@qq.com进行投诉反馈,一经查实,立即删除!