linux信号| 学习信号三步走 | 学习信号需要打通哪些知识脉络?

        前言: 本节内容主要讲解linux下信号的预备知识以及信号的概念, 信号部分我们将会分为几个阶段进行讲解:信号的概念, 信号的产生, 信号的保存。本节主要讲解信号

        ps:本节内容适合学习了进程相关概念的友友们进行观看哦

目录

什么是信号

常见的信号有哪些

进程如何认识信号

前台启动与后台启动

信号的种类

信号的处理方式

键盘数据如何变成信号

针脚

中断向量表


什么是信号

         在我们生活当中, 有许多都是信号的模式。 ——比如我今天点了一个外卖, 我点了之后就在宿舍打游戏了。 之后, 外卖小哥到了, 给我打了一个电话, 敲我的门。 那么这种情况, 就叫做信号产生当我收到这个信号的时候, 我可能有事情, 那么我即便收到了信号, 也不会立即处理, 而是把事情忙完之后, 再进行处理。 也就意味着, 信号暂时得被我记录下来。 ——我们取外卖的动作, 就叫做信号处理; 外卖小哥给我们打电话叫做信号的产生; 而我们可能在做更重要的事情, 比如打游戏, 我们得等一等, 所以我们会把信号产生的这个事情记录下来, 在合适的时候进行处理, 这个就叫做信号保存 整个流程下来, 就叫做信号产生到信号处理的生命周期!

常见的信号有哪些

        我们的上课铃声, 我们的门铃, 取快递,爸爸妈妈都脸色不太好, 古代的冲锋号, 狼烟等等都叫做信号。 

进程如何认识信号

        虽然计算机内部有信号, 但是我们的进程是怎么认识信号的呢?

  •         对于进程来说, 进程一定能够识别并处理信号的能力
  •         并且, 进程即便没有收到信号, 也能知道那些信号该怎么处理。——这个信号处理能力是必须是属于进程内置功能的一部分。
  •         当进程真的收到了一个具体的信号的时候, 进程可能并不会立即处理信号, 因为进程可能在做更重要的事。 
  •         进程当信号产生, 到信号被处理, 这个中间一定有着时间窗口, 所以进程必须有临时保存信号的能力。

        所以, 综上, 我们就知道, 什么叫做认识信号?——认识信号就是能够先识别信号, 然后知道信号的处理方法。 就比如我们人, 我们知道了红灯, 门铃这些信号该干什么, 是因为我们记住了这些信号的识别方法和处理方法。

前台启动与后台启动

我们运行下面这个程序

#include<iostream>
using namespace std;
#include<unistd.h>int main()
{while (true){cout << "i am a crazy process" << endl;}                                                                                                                                                                                  return 0;
}

然后运行结果如下:

        我们除了进程再运行, 其他看不出什么问题。 但是呢, 上面的进程一直在跑, 我们按什么都没有用, 所以这个时候我们ctrl + c, 就可以让进程退出。 

        为了更好的测试我们这里让程序睡眠上一秒钟:

#include<iostream>
using namespace std;
#include<unistd.h>int main()
{while (true){cout << "i am a crazy process" << endl;sleep(1);}                                                                                                                                                                                  return 0;
}

这个程序运行起来了, 但是问题是我们再输入什么都没有用了。 这是为什么?这是因为我们启动的进程是一个前台进程什么是前台进程, 这种我们的进程启动后, 我们的bash进程就不再接收任何指令了, 这种进程, 就叫做前台进程。 

现在, 我们在进程运行的时候输入一个&, 这个的意思是后台进程。

        因为进程的作用就是打印数据, 所以他还会在命令行上打印数据, 并且我们可以在命令行上输入指令, bash会接收这些指令。

        但是问题是我们使用ctrl + c杀不掉这个进程了, 如下图:

这是因为在Linux终端中, 一次登录中, 一个终端, 一般会配上一个bash。 每一个登录, 只允许一个进程是前台进程, 可以允许多个后台进程。 像这种后台进程只能使用kill -9 PID杀掉这个进程。 

        现在我们要说的是, 为什么我们运行一个myprocess后, 再输入ls等指令就没用了呢?——这是因为我们./myprocess默认是前台进程启动。 而我们说过, 每一个登录, 其实只允许一个进程是前台进程。 要知道, 我们的bash也是进程, 而且也是前台进程!那么, 当我们的./myprocess后, 我们的./myprocess就变成了前台进程, 而bash变成了后台进程, 那么再在bash里面输入就没有作用了。 所以, 前台和后台的本质区别就是——谁来获取键盘输入!当我们运行myprocess之后, 获取键盘输入的就是我们的myprocess了, 所以再输入指令没有反应, 因为myprocess不会对指令做出反应!因为bash收不到指令

        再回过头来看我们的这个问题——为什么我们myprocess &, 让进程后台运行之后ctrl + c后杀不掉这个./myprocess进程呢? ——这是因为当我们的后台运行myprocess接收ctrl + c的进程是我们的bash进程, myprocess接收不到命令, 也就不能杀掉这个进程。 

        那么问题又来了, 我们的bash进程ctrl + c后, 为什么不会被干掉呢? 这是因为我们的bash进程内部对于ctrl +c做了特殊的处理。 

        那么, 当我们myprocess后台运行的时候, 随让我们同样可以看到打印, 但是请问这个时候我们可以继续输入指令吗? 

        答案是可以的, 就像上图, 当我们后台运行myprocess进程, 这个时候虽然会继续向显示器上面打印数据, 但是当我们输入指令, 即便这个指令会被数据断开, 但是bash命令会忽略这些后台打印的数据。 

         也就是说, 表面上看我们的指令是被扰乱的, 但是其实这些指令并没有被扰乱, 这个指令还是完整的输给了前台进程。 我们为什么看到的是乱的仅仅是因为我们在进行输入的时候, 字符在显示器上面被回显出来了, 我们在输出上面互相干扰了。 ——我们输入有输入缓冲区, 输出有输出缓冲区输入缓冲区也就是bash的输入缓冲区, 我们使用ls命令, 本质上是ls输入到bash的输入缓冲区当中。 并且, 不光光是给输入缓冲区了, 同时也给了输出缓冲区显示器上面给我们回显了。 

       每当我们登录上一个终端, 每一个终端都会配上一个bash。 一般情况下,bash是前台进程, 他会获取终端的键盘输入, 但是当我们执行一个进程的时候, 它默认会变成一个前台进程, bash会变成一个后台。 整个的在我们的进程一次登录当中, 只允许一个进程是前台, 多个进程是后台。 

        而之所以我们的ctrl + c能够杀掉前台进程, 是为什么——ctrl + c是我们的键盘输入, 是被前台进程收到的。 ——所以在进程ctrl + c的时候, 本质是被前台进程解释成为收到了信号,这个信号是2号信号。 

信号的种类

        kill -l可以查看我们当前的linux系统一共支持多少信号。 ——注意, 没有0号信号, 没有32, 33好信号。 一共62个信号。 其中, 1 ~31被称为普通信号,后面的信号被称为实时信号。 什么是实时信号——我们说过, 一个信号产生了, 那么我们可以不去立即处理, 这种叫做普通信号, 而一旦信号产生了, 我们必须尽快处理, 这种信号被称为实时信号。  

        我们的ctrl + c 其实就是出发了二号信号——SIGINT.

信号的处理方式

        当我们收到一个信号的时候, 我们会在合适的时候处理这个信号。 信号的处理方式是什么——

  •         就比如当我们路过红路灯的时候, 遇到绿灯, 我们会走, 红灯我们会停下来——这就是我们处理信号的默认动作;
  •         有一些人看到红灯之后不管红灯, 直接闯红灯——这就叫忽略;
  •         红灯亮的时候, 有些人天生就是显现包, 红灯一亮, 他就唱歌, 他就跳舞等等, 这些动作是他们自己按照自己的想法来的——这就叫做自定义动作。 这种自定义动作通常叫做信号的捕捉。 

        我们上面说的收到二号信号的默认动作, 就是终止自己。 

如何捕捉信号呢?就是使用下面的函数:

        这里的signal函数, 谁要调用它, 就要传送两个参数, 第一个参数就是上面的signal标号。 而后面的hander就是谁调用这个函数, 执行这个信号的自定义捕捉动作!!! 未来如果我们想收到一个信号, 并在收到这个信号时, 让他自定义去执行动作, 就把要执行的动作和信号通过这个函数进行绑定!——即这个函数, 是用来修改特性进程对信号的处理工作的。 

        第一个参数是int类型, 传送的是信号的编号。 确定的是哪一个信号。 所以, 我们就要传送这个信号的编号。 下面是宏定义:

        然后, 定义下面的代码,并执行:

        然后我们就会发现, 原本的ctrl + c会直接退出, 但是现在不退出了, 而是执行我们的自定义的动作。 那么我们就验证了, 我们的ctrl + c其实就是要发送信号。 并且默认动作和自定义动作只会执行一个。 

        注:想要让它退出, 就是要使用exit调用。 

        还有一个问题就是为什么我们使用signal的时候, 要把它放在一开始, 而不是放在后面或者中间位置呢? 

        这是因为signal只需要设置一次, 往后进程的生命周期里面, 所有的都有效。那么问题是——我们的hander方法是我们的在调用signal函数的时候就调用的还是后面遇到信号的时候再调用的呢? 就比如我们和别人做约定, 我们需要遇到红灯就唱歌。那么我们是定下约定的时候就唱歌呢? 还是遇到红灯再唱歌呢?一定是我们遇到红灯时再唱歌, 那么是不是如果看不到红灯, 那么就永远不需要执行约定。——类似的, 也就是说, 我们后续没有收到这个信号 那么这个新创建的方法, 就永远也不需要调用

        注:有些信号可以被捕捉, 有些不可以被捕捉。 比如9号,19号信号。 

键盘数据如何变成信号

        根据冯诺依曼体系结构, 我们的进程是不能直接访问硬件, 也就是键盘的。 它必须由它的管理者直接访问, 所以我们键盘的按下, 肯定是由操作系统第一个知道的。 ——操作系统是如何知道键盘被按下, 并且把键盘里面的数据输入到自己里面的呢? 也可以换一个说法就是操作系统是怎么知道键盘上有数据了的呢?现在看下面一张图:

        想要知道键盘有数据, 最好的方法就是操作系统定期去检查这个键盘。 因为linux下一切皆文件, 所以键盘也是文件键盘也有自己的文件描述符有自己的内核缓冲区键盘读取的本质, 就是把键盘硬件的数据拷贝到键盘文件的缓冲区里面。 

        可是计算机中的外设太多, 操作系统怎么知道我们应该向哪个外设中拿到什么数据呢?另外, 我们知道, 我们的cpu, 虽然是不和外设直接打交道的, 但是这是在数据层面。 在控制层面, 我们的cpu还是能读懂外设的。 如何读懂外设? 就是利用cpu周边的针脚, 利用一种叫做中断号的概念!    

针脚

  •         我们的cpu上面有很多很多的针脚, 这些针脚是集成在主板上面的, 而我们的键盘显示器内存, 各种设备也是插在主板上面的。 我们对应的键盘, 是直接能够和cpu连接到的。 我们虽然cpu不从键盘当中读取数据, 但是键盘能够从硬件层面上发送一个硬件中断。 
  •         也就是说, 操作系统在进行我们工作的时候, 就忙自己的, 但是一旦键盘上面有数据, 键盘就会通过硬件单元, 把我们的键盘当中的信息发送给cpu。
  •         那么我们要知道什么? 我们要知道的是, 从外设到内存, 再到cpu, 轮询的检查, 对于操作系统的负担是很大的。 所以操作系统不会去检查硬件里面是否有数据, 而是硬件有数据了, 通过硬件中断, 来给我们的cpu发送中断, 来让操作系统完成拷贝。 每一种中断, 都有一种中断号的概念。        
  •         这个中断号及类似于123456789这种数字, 假如我们的键盘的中断号是1, 未来他有数据, 就直接通过cpu连接的针脚, 向cpu内发送某个中断号, 然后针脚接收中断号的高低电频, 由cpu来解释这些电频, 确定中断号是几。 所以, 也就是说, 当键盘中有数据, cpu就能获取这个键盘的中断号, 他就记下来了。——这个过程更进一步理解就是:我们知道cpu里面有寄存器, 可以保存数据。 cpu中的寄存器凭什么可以保存数据? 本质上就是对我们的寄存器在充放电的过程,键盘给cpu发送高低电频,我们的针脚能够识别这些高低电频,  而cpu里面的寄存器里面有一个个硬件单元,这些高低电频给寄存器里面的硬件单元充放电。 并且这些电频有高有低, 到了软件层面,就被解释成为了一个个二进制。 

中断向量表

        在我们的软件层面上, 我们的操作系统内, 比较靠前的位置, 当操作系统开机的时候, 就会生成一张中断向量表。

        这个中断向量表里面是方法的地址, 主要是包括直接访问外设的方法, 主要是磁盘, 还有各种显示器, 键盘的设备。 这里面其实放着的就是函数指针, 所以他会指向操作系统当中的某个位置。 

        所以, 我们的外设, 一旦获取了数据, 就会通过中断单元, 将数据写到cpu的寄存器当中, cpu的寄存器就获得了一个中断号, cpu拿着中断号去操作系统的中断向量表当中寻找外设的方法。 然后执行这个方法, 而这个方法, 才是我们的数据从外设拷贝到内存中的方法。 操作系统在将数据从外设拿到内存的时候就会判断。 判断这个数据是数据还是控制, 如果是控制, 比如ctrl + c。呢么操作系统就会把ctrl + c转化为2号信号, 发送给进程。 进程进而直接终止。 

这里有一个概念, 叫做信号的产生和我们的代码的运行是异步的。 这是为什么? 
        就比如我们老师给我们上课, 如果老师这个时候口渴了, 想要喝水, 他就让学生A去买水。 这个时候老师就和全班同学说先上自习, 等张三回来再上课。 这个过程, 就叫做同步的。 但是第二天, 老师又口渴了, 老师又让学生A去买水, 但是这个老师这次开始不等张三了, 他就继续上课。 张三呢, 随时随地可能回来, 老师不管, 老师就做他自己的工作。 两人之间你做你的, 我做我的, 互不干扰, 这叫做异步。 ——也就是说, 我们今天再运行我们的代码的时候, 他什么时候产生我们并不知道。 他可能随时随地地产生, 但是我们不需要等待这个信号。 这就说信号和我们的代码的执行是异步的。 ——这就有了信号的概念——信号是进程之间时间异步通知的一种方式, 属于软中断!

——————以上就是本节全部内容哦, 如果对友友们有帮助的话可以关注博主, 方便学习更多知识哦!!!

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

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

相关文章

电脑上数据丢了怎么找回来 Win系统误删文件如何恢复

无论是在工作中&#xff0c;还是生活中&#xff0c;电脑都是不可缺少的重要工具&#xff0c;尤其是在工作中&#xff0c;电脑不仅可以高效的完成工作&#xff0c;还可以存储工作中的重要资料。不过在使用电脑的时候&#xff0c;也会遇到数据丢失的情况。针对这一问题&#xff0…

水面巡检船垃圾漂浮物检测系统源码分享

水面巡检船垃圾漂浮物检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of …

在GPU计算型实例中安装Tesla驱动超详细过程

摘要&#xff1a;在深度学习、AI等通用计算业务场景或者OpenGL、Direct3D、云游戏等图形加速场景下&#xff0c;安装了Tesla驱动的GPU才可以发挥高性能计算能力&#xff0c;或提供更流畅的图形显示效果。如果您在创建GPU计算型实例&#xff08;Linux&#xff09;时未同时安装Te…

Linux服务器安装Anaconda环境

Linux浪潮云服务器安装Anaconda环境 读研之后在导师的帮助下&#xff0c;获得了浪潮的一台公共云服务器。以后做实验跑代码就可以使用云服务器上的虚拟环境了。减少了自己笔记本的压力。在创建并保存完成镜像环境之后。最重要的就是安装好深度学习需要的Anaconda环境&#xff0…

“类型名称”在Go语言规范中的演变

Go语言规范&#xff08;The Go Programming Language Specification&#xff09;[1]是Go语言的核心文档&#xff0c;定义了该语言的语法、类型系统和运行时行为。Go语言规范的存在使得开发者在实现Go编译器时可以依赖一致的标准&#xff0c;它确保了语言的稳定性和一致性&#…

python -tkinter

在Button的command绑定中&#xff0c;如果给的一个函数&#xff0c;则表示执行一次。 import tkinter as Tkfrom tkinter import messageboxi 0def myLabel():global root,is Tk.Label(root,text"the import thing is :" )s.pack()root Tk.Tk()def fun1():if mess…

[PICO VR眼镜]眼动追踪串流Unity开发与使用方法,眼动追踪打包报错问题解决(Eye Tracking/手势跟踪)

前言 最近在做一个工作需要用到PICO4 Enterprise VR头盔里的眼动追踪功能&#xff0c;但是遇到了如下问题&#xff1a; 在Unity里面没法串流调试眼动追踪功能&#xff0c;根本获取不到Device&#xff0c;只能将整个场景build成APK&#xff0c;安装到头盔里&#xff0c;才能在…

【Python语言初识(五)】

一、文件和异常 在Python中实现文件的读写操作其实非常简单&#xff0c;通过Python内置的open函数&#xff0c;我们可以指定文件名、操作模式、编码信息等来获得操作文件的对象&#xff0c;接下来就可以对文件进行读写操作了。这里所说的操作模式是指要打开什么样的文件&#…

泛型(Java)

1.泛型&#xff1a; 将数据类型作为参数进行传递。(传递的数据类型必须是引用数据类型) 本质是参数化类型。 泛型集合&#xff1a;可以约束集合内的元素类型 典型泛型集合ArrayList<E>、HashMap<K,V> <E>、<K,V>表示该泛型集合中的元素类型泛型集合中的…

计算机视觉学习路线

计算机视觉&#xff08;Computer Vision&#xff09;是计算机科学的一个重要分支&#xff0c;旨在使计算机能够理解和解释视觉数据。以下是一个详细的计算机视觉学习路线&#xff0c;帮你系统地掌握这个领域所需的知识和技能。 1. 基础数学和编程 在深入学习计算机视觉之前&…

引入Scrum激发研发体系活力

引言 在当今快速变化的技术环境中&#xff0c;IT企业面临着持续的市场压力和竞争&#xff0c;传统的瀑布式开发模式已经难以满足现代企业的需要。瀑布模型过于僵化&#xff0c;缺乏灵活性&#xff0c;导致项目经常延期&#xff0c;成本增加&#xff0c;最终可能无法达到预期效果…

【Qualcomm】高通SNPE框架的使用 | 原始模型转换为量化的DLC文件 | 在Android的DSP端运行模型

目录 ① 激活snpe环境 ② 设置环境变量 ③ 模型转换 ④ run 首先&#xff0c;默认SNPE工具已经下载并且Setup相关工作均已完成。同时&#xff0c;拥有原始模型文件&#xff0c;本文使用的模型文件为SNPE 框架示例的inception_v3_2016_08_28_frozen.pb文件。image_file_list…

数据集-目标检测系列-口罩检测数据集 mask>> DataBall

数据集-目标检测系列-口罩检测数据集 mask>> DataBall 数据集-目标检测系列-口罩检测数据集 mask 数据量&#xff1a;1W DataBall 助力快速掌握数据集的信息和使用方式&#xff0c;享有百种数据集&#xff0c;持续增加中。 数据项目地址&#xff1a; gitcode: https…

【Python报错已解决】TypeError: list indices must be integers or slices, not str

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 专栏介绍 在软件开发和日常使用中&#xff0c;BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…

打造同城O2O平台:外卖跑腿APP的架构与功能设计详解

今天&#xff0c;小编将于大家共同讨论外卖跑腿APP的架构设计及其核心功能&#xff0c;旨在为开发者提供一份详尽的参考。 一、外卖跑腿APP的架构设计 1.整体架构概述 通常包括前端、后端和数据库。 2.前端设计 用户端提供直观的界面&#xff0c;方便用户下单、查询订单状态…

初学51单片机之I2C总线与E2PROM

首先先推荐B站的I2C相关的视频I2C入门第一节-I2C的基本工作原理_哔哩哔哩_bilibili 看完视频估计就大概知道怎么操作I2C了&#xff0c;他的LCD1602讲的也很不错&#xff0c;把数据建立tsp和数据保持thd&#xff0c;比喻成拍照时候的摆pose和按快门两个过程&#xff0c;感觉还是…

C语言实现归并排序(Merge Sort)

目录 一、递归实现归并排序 1. 归并排序的基本步骤 2.动图演示 3.基本思路 4.代码 二、非递归实现 1.部分代码 2.代码分析 修正后代码&#xff1a; 归并过程打印 性能分析 复杂度分析 归并排序是一种高效的排序算法&#xff0c;采用分治法&#xff08;Divide and Con…

javase复习day35反射

反射 获取class对象的方法 public class Demo1 {public static void main(String[] args) throws ClassNotFoundException {//获取反射的三种方式//第一种 Class.forName(全类名)//用法&#xff1a;最为常用Class<?> clazz1 Class.forName("Reflection.Student&q…

程序员如何以最快的方式提升自己?分享4个有效方法!

作家周国平说&#xff1a;人与人之间最重要的区别&#xff0c;不在物质的贫富和社会方面的境遇&#xff0c;是内在的素质和层次&#xff0c;把人分出了伟大与渺小、优秀与平庸。有的人醉心于三五成群的消遣&#xff0c;有的人专注于一步一脚印的努力&#xff0c;人和人之间的差…

Shiro-550—漏洞分析(CVE-2016-4437)

文章目录 漏洞原理源码分析加密过程解密过程 漏洞复现 漏洞原理 Shiro-550(CVE-2016-4437)反序列化漏洞 在调试cookie加密过程的时候发现开发者将AES用来加密的密钥硬编码了&#xff0c;并且所以导致我们拿到密钥后可以精心构造恶意payload替换cookie&#xff0c;然后让后台最…