Linux——进程信号的发送

news2024/11/15 4:52:07

目录

一.信号发送的概念

首先来讲几个发送术语:

它有三种情况:

 注意:

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

三.信号捕捉

 所以总结一下:

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

问题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(忽略方式),以内核态身份就可以处理,然后就可以直接返回到用户代码中系统调用的位置,少了两次身份的转变,如下图:

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

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

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

相关文章

DuDuTalk:4G录音工牌如何帮企业实现,线下服务的远程实时监督?

数字化时代,企业越来越重视客户体验。而线下服务是企业的员工跟客户直接互动最重要的环节,这直接关乎到客户对品牌、产品、服务的最直观评价和反馈,最终影响客户的购买和成交。 但企业的线下服务场景往往又是企业数字化最薄弱的一环&#xf…

Qt,day4

闹钟 #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget) {ui->setupUi(this);this->setWindowTitle("闹钟");this->setWindowIcon(QIcon("D:\\HQYJRJ\\QT\\day1\\…

【Python爬虫开发基础⑫】requests库概述(文件上传、cookies处理、状态码处理、异常处理等)

🚀个人主页:为梦而生~ 关注我一起学习吧! 💡专栏:python网络爬虫从基础到实战 欢迎订阅!后面的内容会越来越有意思~ 💡往期推荐: ⭐️前面比较重要的基础内容: 【Python爬…

数据平台之数仓模型设计

文章目录 前言一、维度建模基本概念1.1 事实表1.2 维度表 二、维度建模三种模式2.1 星型模型2.2 雪花模式2.3 星座模式 三、ChatGPT代替Sql Boy3.1 简单案例3.2 复杂案例 四、总结 前言 看到几篇不错的文章,自己总结合并了分享给小伙伴 金博尔和恩门共同开创的数仓…

vue项目 设置启动时自动运行到电脑默认浏览器中

相信大家很多参与企业开发会发现 别人搭建的vue项目都会自动启动在电脑的默认浏览器上 这个其实React项目自己就会有 但是 vue项目我们需要自己设置一下 在根目录的 vue.config.js 将devServer下 设置 open: true 参考代码如下 module.exports {devServer: {open: true} }这样…

机器学习30:《推荐系统-III》使用 TensorFlow 构建电影推荐系统

本文将介绍基于 MovieLens 数据集创建一个电影推荐系统的方法。具体而言,包括探索电影数据,训练矩阵分解模型,检查嵌入,矩阵分解中的正则化,Softmax 模型训练等内容。 目录 1.准备工作 1.1 导入依赖模块 1.2 加载数…

LeetCode 1107.每日新用户统计

数据准备 Create table If Not Exists Traffic (user_id int, activity ENUM(login, logout, jobs, groups, homepage), activity_date date); Truncate table Traffic; insert into Traffic (user_id, activity, activity_date) values (1, login, 2019-05-01); insert into …

Charles抓包map local后出现“failed: unacceptable content-type: text/plain“

Charles 抓包map local 修改 映射到本地数据 出现如下报错 返回ErrorUrlhttps://xxxx 返回ErrorError Domaincom.alamofire.error.serialization.response Code-1016 "Request failed: unacceptable content-type: text/plain" UserInfo{NSLocalizedDescriptionRequ…

邮票面值-2022年全国青少年信息素养大赛Python国赛第5题

[导读]:超平老师计划推出《全国青少年信息素养大赛Python编程真题解析》50讲,这是超平老师解读Python编程挑战赛真题系列的第7讲。 全国青少年信息素养大赛(原全国青少年电子信息智能创新大赛)是“世界机器人大会青少年机器人设计…

适配理想全系车型,OPPO首创手表无感蓝牙车钥匙系统

OPPO 宣布与理想汽车深度合作,首家推出系统级手表无感蓝牙车钥匙功能,适配理想全系列车型。此功能早已适配 OPPO 手机。仅支持 OPPO Watch 2、OPPO Watch 3 和 OPPO Watch SE 系列,可独立使用,无需手机即可控制车辆。 OPPO首发数字…

Python:创建一个满足高斯分布的立方体

算法说明: (1)首先定义一个中心点坐标 center,标准差 sigma 和峰值 amplitude。 (2)然后通过计算每个点到中心点的欧氏距离,并将欧氏距离应用于高斯分布的公式 amplitude * exp(-distances**2 /…

tcp转发服务桥(windows)

目的 目的是为了在网关上转发udp数据和tcp数据。对于网络里面隔离的内网来说,有一台可以上网的服务器,那么通过两块网卡就可以转发出去,在服务器上进行数据的转发,有tcp和udp两种,udp已经写过了,这次使用了…

MySQL 导出库和表信息导出成Excel

最近在写文档需要将数据库的表和对应的表信息做成EXCEL。 我不能一个一个表一个一个字段的敲下去吧!!! 那有没有一个SQL搞定呢? 这个可以有有! 数据库里有那些表(包含表名和表介绍) SELECT…

路径规划算法:基于野马优化的路径规划算法- 附代码

路径规划算法:基于野马优化的路径规划算法- 附代码 文章目录 路径规划算法:基于野马优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要:本文主要介绍利用智能优化算法野马…

关于CEPH的简单畅谈

CEPH是什么 CEPH是一个先进的分布式存储系统,它具有高度可靠性、可扩展性和性能。CEPH旨在解决传统存储系统中存在的诸多挑战,如单点故障、难以扩展、数据丢失风险等。 CEPH的设计理念是将数据分布到一个由多个节点组成的集群中,并利用冗余…

ERROR: ORA-12560: TNS: 协议适配器错误

之前在Windows安装了Oracle,遇到了ORA-12560 TNS: protocol adapter error的错误。这个问题的原因很简单,就是没有配Oracle的环境变量。由于是去年遇到的问题,我现在已经忘了具体配置什么变量,但可以肯定的是这个问题就是环境变量…

24 | MySQL是怎么保证主备一致的?

以下内容出自《MySQL 实战 45 讲》 https://time.geekbang.org/column/article/76446 24 | MySQL是怎么保证主备一致的? MySQL 主备的基本原理 如图所示就是基本的主备切换流程。(M-S结构) 节点 A 到 B 这条线的内部流程是什么样的 &#x…

DEVICENET转ETHERNET/IP网关devicenet怎么读

远创智控YC-EIP-DNT,你听说过吗?这是一款自主研发的ETHERNET/IP从站功能的通讯网关,它能够连接DEVICENET总线和ETHERNET/IP网络,从而解决生产管理系统中协议不同造成的数据交换互通问题。 这款产品在工业自动化领域可谓是一大利…

微调预训练的 NLP 模型

动动发财的小手,点个赞吧! 针对任何领域微调预训练 NLP 模型的分步指南 简介 在当今世界,预训练 NLP 模型的可用性极大地简化了使用深度学习技术对文本数据的解释。然而,虽然这些模型在一般任务中表现出色,但它们往往缺…

vue进阶----路由

目录 前端路由的概念与原理 什么是路由 SPA 与前端路由 前端路由 前端路由的工作方式 实现简易的前端路由 vue-router 的基本用法 vue-router vue-router 安装和配置的步骤 声明路由的匹配规则 vue-router 的常见用法 1、路由重定向 2、嵌套路由 3、动态路由匹配 …