Linux——进程信号的发送

目录

一.信号发送的概念

首先来讲几个发送术语:

它有三种情况:

 注意:

二.信号在内核中的表示示意图

三.信号捕捉

 所以总结一下:

此时,会出现这样一个疑问:操作系统是如何得知现在被执行的进程是用户态还是内核态?

问题2:CPU在执行某个进程时是如何跑到操作系统中执行代码的呢?

用户态的进程想要转换为内核态的情况大致有3种:

如下为进程的两种状态转换图:

         关于第三步有人就会问了,为什么内核态的进程不能够去执行handler方法,而是还要变成用户态才去执行?

       

        关于第三步完成后,调用handler表中的方法后,为什么不就在用户态中继续往下执行,还要再回一次内核态?


        在上一篇博客中,我以列举生活中的例子为基础,介绍了对信号的理解,信号的4个重点知识,信号的4种产生方式,且信号只能由操作系统发送给进程......

        接下来,我继续来详解一下操作系统是怎么将信号发送出去的!

一.信号发送的概念

首先来讲几个发送术语:

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

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

3.对于操作系统发送来的信号,进程可以选择阻塞(Block) 该信号。

        

        对于信号递达的理解:——是指进程收到信号后将其处理时的动作!所以进程在执行递达时,有三种方式可供选择:

1.执行默认处理;

2.自定义处理(例如:采用signal函数,做想要做的行为);

3.忽略操作(默认不管) 

        信号的产生(可以是由键盘的手动产生:ctrl+c、可以是系统调用kill()函数产生、可以是硬件异常产生:除零错误和段错误等、也可以是软件条件产生的信号:读端关闭写端仍开),所以信号在被OS操作系统指定目标进程发送开始到进程接收信号的这段时间称之为信号未决。

它有三种情况:

1.操作系统向进程发送了信号,进程收到了信号还没来得及处理,该进程可能在做着更重要的事情;

2.操作系统向进程发送了信号,在此途中,进程并没收到信号;

3.操作系统向进程发送了信号,进程收到了信号,但提前进行了对该信号阻塞,导致进程无法递达处理该信号。

 注意:

        被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,进程才会执行递达动作;

        阻塞和忽略是不同的,只要信号被阻塞,那么意味着进程永远不能递达(处理)这个信号;而忽略是进程递达(处理)这个信号的一种方式。

       上图就好比一块电路板,我们打开了电源,插座也插上了灯泡接口,但灯就是不亮,原因在于灯泡中的保险丝已经在通电前就被熔断了——保险丝的熔断阻塞了电流通向灯泡的道路。


进程进行默认处理信号的案例:

 该进程的执行方式就是采用默认的动作,遇到错误就立即终止退出。

进程进行自定义处理信号的案例:

         进程所作的递达动作是:自定义方式,该方式是通过调用signal函数,然后执行该函数的第二参数——回调函数,进而对该信号所想做的一些事情——Checksig();

忽略动作就不展示了......

所以对于信号递达、信号未决、阻塞等术语还不理解的朋友们来看下面这个例子:

        小明是个初中生,当晚自习快结束时,班主任兼物理老师因为家中有急事,给同学们布置了一套卷子,他把卷子放讲台上并让同学们下了自习拿回家做,就急着走了。同学们都记下了这件事,等到下课,小明并没有拿上卷子,转身就骑车回了家。他这么做的原因在于:上次期中考试时,他的物理成绩考的不像样子,被班主任劈头盖脸的骂了一顿,他心生怨恨,根本不想写与物理有关的一切作业。

        根据上面这个例子可知:小明相当于进程,班主任是OS操作系统,是小明的管理者,班主任让小明拿卷子回家做(OS发送信号给进程),小明故意没有拿(进程阻塞了信号的传递),小明只要一直怨恨着班主任,他就一直不做物理卷子(阻塞导致进程无法递达该信号)。

        这种情况就属于阻塞——信号未决的其中一种情况;

        信号未决的第二种表示情况: 小明拿到了物理卷子,但是回家路上出车祸了,住进医院导致没来得及做卷子;

        信号未决的第三种表示情况:老师正要把卷子放到了讲台,还没告知同学们的时候,地震了,老师和同学们就都走了。

        而递达表示:物理老师当天下午又叫小明谈话,这一次他俩冰释前嫌(没了阻塞),晚上发了卷子后,小明拿了物理卷子,回家认认真真做完(递达的默认动作);

        递达的忽略:小明拿了物理卷子,回家后由于数学英语作业明天要讲,导致他没时间做物理卷子;

        递达的自定义:小明拿了物理卷子,回家后开始做卷子,但由于上课没有好好听,导致很多题不会做,瞎写答案。


二.信号在内核中的表示示意图

 根据上图,做出以下理解:

        操作系统在指定目标进程后向该进程发送信号,紧接着该进程的PCB(进程控制块),它的底层内核数据结构:task_struct中的pending(位图结构)、block(位图结构)、handler(函数指针数组)三个属性就会被激活。

        先来看前两个属性,pending未决位图和block阻塞位图,两个位图结构的底层都是由32比特位形成,从右往左由低到高。这32比特位默认情况下都为0;每个比特位都代表了一个信号,例如两个位图最右边的那个比特位是第一比特位,该比特位指的是1号信号,往左依次为2号信号、3号信号.....到第31号信号。

        pending未决位图的作用是显示:OS发送的信号是否被进程所处理。举个例子:由于进程在执行过程中出现了非法错误,使得OS查出错误后向该进程发送了4号信号,该进程的pending位图中从右往左第4个比特位由0--->1,直到该进程收到4号信号并开始处理这4号信号时,pending位图的比特位才会由1-->0。

        block阻塞位图的作用是显示:OS发送的信号是否已被进程所阻塞。举个例子:OS向指定进程发送4号信号,进程提前设置阻塞位图的第4比特位0--->1,该进程收到信号后无法处理4号信号。什么时候进程解除了阻塞,block阻塞位图的第4比特位什么时候才会由1--->0。

        接下来就是第三个属性——handler表,handler是一个函数指针数组,在该数组中,数组的下标表示信号的编号,例如数组下标0标识为1号信号......;数组元素的内容代表着对应信号的处理方法,当我们访问数组中的[0]、[1]、[2]时,访问的是handler的函数指针,每一个函数指针都指向对应信号的处理方法。

        当我们来到handler时,已经能够表明进程已经成功收到了操作系统发来的信号,并正在对其进行递达处理,上面说过,递达分为三种方式:默认、忽略、自定义。在下面这个内核图中:

        第一行的SIGUP信号,它的处理方式为:SIG_DFL(默认处理——宏定义),意味着handler[0]的内容写的是SIG_DFL函数(立即终止进程,并退出);第二行SIGINT信号(2号信号),它的处理方式是SIG_IGN(忽略动作——宏定义),意味着handler[1]的内容写的是SIG_IGN的方式,对该信号的处理方式是不处理。而第三行SIGQUIT信号(3号信号),它的处理方式为用户自己写的自定义方法signal(signo,sighandler)函数,意味着handler[2]中填入的内容是用户采用sighandler函数的函数地址。

         在这里考虑一种特殊情况:当一个进程收到了OS发送来的多个信号时,由于进程只记录一个,所以pending位图一次只能置一个比特位,剩下的信号就会被丢弃。

        注:上面这种情况只针对普通信号的发送,而对于实时信号的发送(Linux中第34——第64号信号),进程会对这些信号组成一个队列,该队列会保存这些信号的属性信息,等待进程一个一个的处理。


三.信号捕捉

        当进程收到信号时,进程不会立即处理,而是在“合适的时候”。这个合适的时候是指:从内核态返回用户态时,进程才会进行处理。

       用户想要访问上图的两种资源,就必须采用系统调用函数,系统调用是操作系统给用户提供的接口。普通用户无法以自己用户态的身份去访问系统调用资源,得先让自己的状态变成内核态才能执行。

        详解如下:

        1.当CPU执行进程自己的代码时,进程当前处于用户态,直到CPU执行了系统调用函数(open、getpid、fork)等,进程就必须转换为内核态,因为这些接口是属于OS的,CPU想要访问别人的资源,就得提升到能和别人平起平坐的身份才有资格。所以状态的转换是必然的。

        在转入内核态后,CPU就可以访问系统的资源了,这时代码中有open函数,于是CPU开始访问该函数,好比它在门外敲了敲open,请求open的帮骂,OS知道后将open的底层实现代码拷贝了一份给CPU。

        即使你的身份是内核态,但操作系统不相信任何人,它只相信它自己!你的身份是用户态便没有访问需求的资格;当你是内核态,有了资格也无法亲自访问,操作系统怕你乱搞,于是它帮你拷贝相应函数的代码给你。

        需要注意的是:这里只是做了状态转换,访问系统接口的人仍然是CPU,只不过身份变了而已。

        2.CPU执行系统调用是费时间的,因为有状态的来回转换,会导致执行效率降低,所以尽量不要频繁的采用系统调用!而自己写的函数往往比函数调用要更快。

 所以总结一下:

用户态:正在执行用户层的代码,此时进程的状态是用户态.

内核态:正在通过系统调用访问内核、硬件资源,此时进程的状态是内核态。


此时,会出现这样一个疑问:操作系统是如何得知现在被执行的进程是用户态还是内核态?

        在CPU中有一套寄存器,寄存器中存放着进程执行代码的数据,之前说过多个磁盘文件被加载到内存后,会被放进运行队列中,计算机之所以能够并发执行多个程序,就是通过时间片轮转的方式,让cpu一段时间内执行进程A,一段时间执行进程B,而寄存器的作用不仅仅用来处理计算,还用来保存上一个进程A被换下后的上下文数据,以遍下次cpu再次执行进程A时,能够瞬间找到上次末尾的位置继续运行该进程。

        而在上图中,某寄存器1用来存放当前被执行进程的PCB地址;某寄存器2用来存放该进程的页表(虚拟空间中数据的虚拟地址可通过页表映射内存的物理地址)地址;而CR3寄存器的作用是表证当前进程的运行级别:

若CR3寄存器为0,表示该进程当前是内核态(高级别);

若CR3寄存器为3,表示该进程当前是用户态(低级别);

 有了CR3寄存器,OS就可以轻易的知道每个进程当前的运行级别是什么状态的!

问题2:CPU在执行某个进程时是如何跑到操作系统中执行代码的呢?

     

        从上图看,进程被加载到内存后,其PCB(task struct)就会被CPU执行。之前学习进程的时候就解释过上图,当CPU执行进程的代码时,PCB(task struct)中的属性指针会指向进程虚拟的地址空间,里面会存放代码中数据的虎拟地址,当CPU访问到某个变量的虑拟地址后,需要通过页表找到物理内存中的该变量的物理地址,找到后将数据返回给CPU。这里说的虑拟地址空间中共有4GB大小,其中从低到高 第0到第3GB的空间为用户级空间,用来存放用户的代码,数据等。为了保证进程的独立性,每个进程都有一个进程地址空间,都有一个用户级页表。
        第3-第4GB的空间为内核空间,那么相对应的也有一份内核级页表,该内核级页表是操作系统的,操作系统只有一个,所以是所有的进程共用这独一份内核级页表。
        其次内核空间也是不允许用户访问的,因为这1GB空间中的数据是通过内核级页表和内存中的操作系统相映射,属于内核级别的。因为内存中只存在一份内核,那么所有进程的虚拟地址空间中这1GB的内核空间都通过同一份内核级页表和内存中的内核相映射。 

        即每一个进程地址空间中的内核空间都是一样的,因为它们都通过同一个内核级页表和内存中的OS相互映射。

        有了以上这些知识的铺垫,我们就可以理解CPU是如何跑到OS中访问其资源的:

        首先用户写了一份代码,CPU将其运行,现状态为用户态,在CPU执行的代码中有了系统调用接口,在CPU调用系统接口时进程转换为内核态,CPU寄存器CR3的值自动转换为0,OS发现该进程的CR3为0,验证成功,允许CPU访问系统资源,且CPU自动从进程地址空间的用户空间跳转到内核空间,从而申请系统资源进行访问,因为此时访问的还是接口的虚拟地址,需要通过内核级页表去映射内存中相应的物理地址,最终找到后返回CPU。调用完系统接口后,CPU再回到用户空间继续执行下面的代码。      

      


用户态的进程想要转换为内核态的情况大致有3种:

1.执行系统调用函数;

2.进程在执行过程种出现了中断、异常、缺陷;

3.进程之间的相互切换

解析:执行系统调用上面已经说的很清楚了;
        对于第2点,进程出现了中断异常,即表明CPU在执行代码过程发现了代码上的错误(越界,除零...)然后报告给OS,OS根据错误向进程发送信号......当进程出现中断时,进程由用户态转换为内核态,然后操作系统成为执行者,之后才能做一系列的操作(查出错误、发送信号),发送完后进程再转换为用户态,然后根据发来的信号对出现中断,异常的地方进行处理,处理完后继续往下执行代码。
        对于第三点进程切换,说自了比如有3个程序被加载到内存成为进程,这三个进程被OS放进运行队列中,CPU通过时间片轮转的方式依次执行这三个进程,当一个进程被CPU执行时,该进程转为用户态,执行用户写的代码,执行几秒后,该进程被转换为内核态,被操作系统从CPU中剥离到运行队列,等待下一次的运行。等到下一次再被CPU执行时,再转为运行态...

如下为进程的两种状态转换图:

 

顺序如下: 

第一步:用户态进程的CPU需要访问open系统调用函数时,转入内核态访问。

第二步:从用户空间跳转到内核空间后,访问到open函数的虚拟地址,再通过内核级页表访问到该函数的物理地址。紧接着CPU通过进程的两个位图和handler数组表,查看有没有信号递达。

第三步:在检查的过程中第四行的信号正处于未决状态,handler表中采用的是自定义方式处理(这时进程仍是内核态),自定义方式为signal(signo, handler)函数调用,进程需要转换为用户态去执行signal方法。

         关于第三步有人就会问了,为什么内核态的进程不能够去执行handler方法,而是还要变成用户态才去执行?

        在理论上,内核态进程是完全可以执行signal函数的,但在实际操作上是不被允许的,原因在于:操作系统不相信任何人,万一用户写的代码是恶意的、非法代码,用内核态(被操作系统任何)的身份去执行,就会导致安全问题。所以必须经过特定的方式切换到用户态身份去执行自定义处理的方式才能保证系统的安全。

第四步:执行完signal方法后,需要带着数据再转换回内核态,此时,检查信号是否递达的工作已然做完。

第五步:进程返回用户态,继续执行open()函数之后的代码。

       

        关于第三步完成后,调用handler表中的方法后,为什么不就在用户态中继续往下执行,还要再回一次内核态?

        原因:因为进程的上下文信息是系统保存的,是紧密相关的,进程不能由一种状态直接跳回另一种状态,这时需要操作系统帮助才能的,需要操作系统就得让进程经过特定的方式回到内核态。

简化上图的过程: 

       绿色圆圈表示进程的两种身份的切换,共有4次。上图可以看成是一个无穷大的符号。线上的是用户态,线下的是内核态。

        况且上面这种是进程状态转换最复杂的情况,因为是自定义的操作,第三步和第四步的过程就导致状态转换多了两次;

        若是SIG_DFL(默认处理方式)和SIG_IGN(忽略方式),以内核态身份就可以处理,然后就可以直接返回到用户代码中系统调用的位置,少了两次身份的转变,如下图:

        默认和忽略动作只有两次的身份切换。这两种方式是程序员写入到操作系统中的宏定义,是被操作系统所信任的方式,那在第三步的过程中就直接在内核态处理完毕,然后直接返回用户态了。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Spring Cloud Alibaba Seata 处理分布式事务】——每天一点小知识

💧 S p r i n g C l o u d A l i b a b a S e a t a 处理分布式事务 \color{#FF1493}{Spring Cloud Alibaba Seata 处理分布式事务} SpringCloudAlibabaSeata处理分布式事务💧 🌷 仰望天空,妳我亦是行人.✨ &#x1f98…

SpringCloud(4) Eureka 如何主动下线服务节点

目录 1.直接停掉客户端服务2.发送HTTP请求1)调用DELETE接口2)调用状态变更接口 3.客户端主动通知注册中心下线1)代码示例2)补充3)测试 一共有三种从 Eureka 注册中心剔除服务的方式: 1.直接停掉客户端服务…

Unity Obfuscator

官方仓库 学习日期:2023-07-13(防止后续仓库特性或功能更新无对比时间) 目标:本文介绍使用此github库,混淆unity项目的代码,在ILSpy中无法正确反编译。 一、说明 官方说明 配置界面 Features: ControlFlow…

【Spring Boot】单元测试

单元测试 单元测试在日常项目开发中必不可少,Spring Boot提供了完善的单元测试框架和工具用于测试开发的应用。接下来介绍Spring Boot为单元测试提供了哪些支持,以及如何在Spring Boot项目中进行单元测试。 1.Spring Boot集成单元测试 单元测试主要用…

LabVIEW FPGA利用响应式数字电子板快速开发空间应用程序

LabVIEW FPGA利用响应式数字电子板快速开发空间应用程序 与传统的基于文本的语言相比,LabVIEW的编程和设计已被证明可以缩短开发时间。各种研究表明,生产率的提高在3到10倍之间。LabVIEW通过图形语言、集成开发环境和多个编译器的组合来实现这一点。 图…

Django_发送邮件

目录 一、开启SMTP服务并获取授权码 二、在Django的配置文件中添加邮箱服务配置 三、发送邮箱代码 源码等资料获取方法 使用django邮箱功能需要搭建smtp服务器,如果没有,可以使用第三方smtp服务器。 本文以第三方QQ邮箱服务器演示如何使用python的s…

接口的幂等性如何设计

前言 所谓幂等: 多次调用方法或者接口不会改变业务状态,可以保证重复调用的结果和单次调用的结果一致。 我们在开发中主要操作也就是CURD,其中读取操作和删除操作是天然幂等的,我们所关心的就是创建操作、更新操作。 创建操作一定是非幂等的因为要涉及…

SpringBoot 如何使用 MockMvc 进行 Web 集成测试

SpringBoot 如何使用 MockMvc 进行 Web 集成测试 介绍 SpringBoot 是一个流行的 Java Web 开发框架,它提供了一些强大的工具和库,使得开发 Web 应用程序变得更加容易。其中之一是 MockMvc,它提供了一种测试 SpringBoot Web 应用程序的方式&…

(EMQX)STM32L+BC20+MQTT协议传输温湿度,ADC,电压,GPS数据到EMQX

1、材料准备 准备以下材料 2、设备连接 2.1 插入物联网卡,天线 首先把BC20核心板从开发板上拆下来 然后将物联卡放置在BC20核心板内 物联卡放置完成将BC20核心板重新插入到开发板内(注意不要弄错方向) 同时接入天线 2.2 连接ST-Link仿真…

RabbitMQ系列(27)--RabbitMQ使用Federation Exchange(联邦交换机)解决异地访问延迟问题

前言: (broker北京)、(broker深圳)彼此之间相距甚远,网络延迟是一个不得不面对的问题。有一个在北京的业务(Client北京)需要连接(broker北京),向其中的交换器exchangeA发送消息,此时的网络延迟很小,(Client北京)可以迅速将消息发…

嵌入式QT- QT使用MQTT

目录 一、MQTT介绍 二、MQTT概念 2.1 订阅(Subscribtion) 2.2 会话(Session) 2.3 主题名(Topic Name) 2.4 主题筛选器(Topic Filter) 2.5 消息订阅 三、MQTT中的角色 3.1 客户端 3.2 服务器 四、X86平…

每个前端开发者都应知道的10个实用网站

1.Documatic Documatic 地址:www.documatic.com/ Documatic 是一个高效的搜索引擎工具,旨在帮助开发人员轻松搜索他们的代码库,找到特定的代码片段、函数、方法和其他相关信息。该工具旨在通过在几秒钟内快速提供准确和相关的搜索结果&…

CSDN博客运营团队2023年H1总结

一、运营工作的指导思路 CSDN的核心价值之一是帮助开发者/技术人快速的学习成长和解决技术问题。这个核心价值的建立离不开无数开发者的无私贡献,而运营工作的核心就是要保证这个价值链不断放大:让内容贡献者体验更好,付出有回报&#xff0c…

Maya适合哪个工作站?

Autodesk Maya 提供多种功能,可以适应电影、游戏和建筑等不同行业的需求。定制的 Autodesk Maya 工作站可以帮助您提高行业领先的 3D 计算机动画、建模、模拟和渲染软件的工作效率和用户体验。 根据您的特定需求定制的快速、强大的工作站可以帮助您充分利用 Maya 工…

python脚本(Word转PDF格式小工具)

from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QMessageBox from PyQt5.QtCore import Qt import sys import os from comtypes import client#文件转换脚本 class FileDropWidget(QWidget):def __init__(self):super().__init__()self.initUI()de…

【SQL应知应会】行列转换(二)• MySQL版

欢迎来到爱书不爱输的程序猿的博客, 本博客致力于知识分享,与更多的人进行学习交流 本文收录于SQL应知应会专栏,本专栏主要用于记录对于数据库的一些学习,有基础也有进阶,有MySQL也有Oracle 行列转换 • Mysql版 前言一、MySQL行列转换1.数据…

LayUI入门,以及介绍

一.LayUI基本情况 1.LayUI介绍 Layui(流行于 layui.com)是一款轻量级的前端UI框架,专注于提供简洁、易用、灵活的界面组件和交互体验。它基于HTML5和CSS3技术,采用模块化开发的思想,提供了丰富的UI组件、常用的工具函数…

一套完全开源,支持多租户,界面配置单点的后端框架JVS

JVS的多租户体系统 在IT系统中,“租户”(tenant)通常用于指代一种多租户架构(multi-tenancy),它是一种软件架构模式,允许多个用户或组织共享相同的应用程序或系统实例,但彼此之间的…

短视频抖音账号矩阵系统源码---功能架构示例1

一、短视频账号矩阵系统源码开发服务器集群化处理开发成本更低(前提) 什么是集群化处理视频内存内耗?集群化处理视频内存内耗是指通过建立集群系统,将视频处理任务分配给多个计算节点进行并行处理,以减少单个计算节点…

PHP 音乐欣赏网站mysql数据库web结构apache计算机软件工程网页wamp

一、源码特点 PHP音乐欣赏网站 是一套完善的web设计系统,对理解php编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。 代码下载 https://download.csdn.net/download/qq_41221322/88041034https://download.…