Linux 信号

文章目录

  • 1. 信号
    • 1.1 前言
    • 1.2 信号的位置
    • 1.3 接口
      • 1.3.1 sigset_t
      • 1.3.2 信号集操作接口
      • 1.3.3 signal
      • 1.3.4 sigprocmask
      • 1.3.5 sigpending
  • 2. 信号的处理
    • 2.1 内核态和用户态
    • 2.2 信号的监测和处理

1. 信号

1.1 前言

在 Linux 中,信号是一种用于进程之间的通信机制,通常是异步的,也就是进程随时都可以收到信号,可以通过信号来通知进程发生了什么事,并且进程可以马上对这个信号做出反应和处理

在 Linux 中输入 kill -l可以得到信号的编号以及这些信号大概代表的是什么,常见的比如 9 号信号就是常见的强制杀死进程信号,以及 11 号就是段错误,具体点就是空指针,指针越界这些异常了,还有Ctrl + C 终止信号是 2 号信号。
(注意:信号是从 1 开始的,没有 0 号信号)

在这里插入图片描述虽然总共有 64 个信号,但是前 32 个信号被称为核心信号,现在更关注的也是这前 32 个信号,用的也很频繁,通常用于程序的各种异常问题

1.2 信号的位置

操作系统随时都可能会给进程发送信号,而且还可能连续发送,进程收到信号之后,可能也不会马上处理,手头可能有优先级更高的事,甚至有可能直接忽略这个信号

先来了解一下信号的三种状态

① 递达(Delivery

  • 当进程收到信号之后,就直接执行该信号的处理方法,称为 信号递达,也就是 处理信号

② 未决(Pending

  • 当信号到达的时候,进程不会马上处理这个信号,先放着,称为 未决,即暂时不处理

③ 阻塞 (Block

  • 如果某个信号被设为阻塞,那么进程收到这个信号的时候,会将这个信号置为 未决 状态,并且阻塞如果不取消,这个信号会一直得不到处理

task_struct 中,可以理解成存储了关于 32 个信号对应的 3 个数据结构,这三结构就是 blockpendingdelivery。并且 pendingblock 可以理解为位图

① 比方说,1 号信号在 pending 位图中对应的标志位为 1,那么说明这个信号暂时不被处理,但是当操作系统识别到这个 1 的时候,就会执行相应的处理函数(如果没被屏蔽的话)
delivery 结构存储了关于这些信号的处理方法
block 中如果对应的 1 号信号为 1,那么说明接下来进程如果收到 1 号信号,那么会将 pending 中关于 1 号信号对应的标志位设为 1,表示 未决,暂时不处理

在这里插入图片描述

其中,delivery存储了某个信号对应的处理方法,⭐「处理方法」包括三种:默认,忽略(不处理),用户自定义。并且 Linux 为默认和忽略提供了宏:默认 —— SIG_DFL忽略 —— SIG_IGN,而每个信号都有各自默认的处理方法

1.3 接口

1.3.1 sigset_t

首先认识一个数据结构 sigset_t,这个数据结构可以用来表示一个信号集,可以表示某个信号的状态。

  • 可以理解成一个位图,比方说,1 号信号在里面的比特位为1,那么表示有效;反之,0 表示无效,或者说没有这个信号

并且这个结构可以用来表示 blockpending,意义也是一样的,比如 sigset_t pendings,如果 1 号信号在里面的标志位为 1,那么表示 1 号信号在这个进程中的状态是未决的

1.3.2 信号集操作接口

1. sigemptyset(sigset_t* set) 					清空信号集,全部置0就对了
2. sigfillset(sigset_t* set) 					将信号集全置 1
3. sigaddset(sigset_t* set, int signo)  		将某个信号在这个信号集中标记为 有效
4. sigdelset(sigset_t* set, int signo) 			将某个信号在这个信号集中标记为 无效
5. sigismember(const sigset_t set, int signo) 	这个信号在信号集中是否 有效

这里面就是各种对 sigset_t 的操作了,后面举例子

1.3.3 signal

在这里插入图片描述上面不是说信号的处理方法有三种吗,而自定义处理方法就可以通过这个函数来指定

  • signum 表示要处理的信号,handler表示函数指针,就是自己定的函数了

(sigaction接口也可以修改信号对应的处理方案,signal 相对于它来说是简化版的)

举个例子:在进程中设置对 2 号信号的捕捉,捕捉之后打印一段话。之后对这个进程发送 2 号信号,这时候就会执行我们绑定的自定义函数了

在这里插入图片描述

1.3.4 sigprocmask

在这里插入图片描述作用:修改进程的阻塞信号集(block

how 参数常见的可以有 3 个值,假设当前进程的 信号屏蔽集为 block

  • SIG_BLOCK:往阻塞信号集中添加set中有效的信号,相当于让当前进程的 block | set
  • SIG_UNBLCOK:往阻塞信号集中去掉 set 中有效的信号,相当于让当前进程的 block | ~set
  • SIG_SETMARK,让当前进程的 block = set

oset 是输出型参数,如果不为空,那么会返回变化之前的 block ,便于后续恢复;如果不关心之前的状态,设为 NULL 就好了

举个例子
在这里插入图片描述屏蔽之后 2 号信号怎么处理?当然可以在程序中定期扫描没有处理的信号,也可以将 2 号信号的屏蔽字恢复为 0,比如:在后面将程序对 2 号信号的屏蔽取消之后,这个进程就会被终止了

在这里插入图片描述
可以看出虽然 pending2 号信号有效,但是由于屏蔽字,2 号信号的处理被拖延了。但是我们一旦取消屏蔽,而pending2号依然有效,2 号信号的处理函数就会被执行

1.3.5 sigpending

在这里插入图片描述
这个很简单了

  • set:输出型参数,会得到进程的 pending 集合,返回 0 表示一切顺利

2. 信号的处理

当进程收到信号之后,pending 中对应的标志位会被置为 1,然后⭐进程每次陷入内核态,再从内核态切换回用户态之前,内核大多会检查进程的信号状态,以处理这些未处理的信号,处理完后,对应标志位置为 0

先了解一下内核态和用户态

2.1 内核态和用户态

还记得虚拟地址空间吗,在 32 位下,虚拟地址空间中有 1G 内核空间,但是这部分空间不使用普通的页表,这 1G内核空间有自己独有的页表 —— 内核级页表,不同于用户空间的页表中 —— 每个进程都有一个自己的用户页表,而内核级页表所有进程共享,操作系统自己的数据和代码就是通过内核页表映射到物理内存上的

操作系统的代码数据必然不允许被随便访问,所以内核态页表带有权限验证。

  • 而进程的内核态和用户态其实就相当于两个身份,如果进程是内核态,那么也就可以访问内核的页表,也就可以访问所有内核代码数据,用户级的代码和数据当然也可以访问。内核态是特权模式,可以访问操作系统的数据和代码,也可以访问硬件设备,执行速度较快。

在这里插入图片描述

当进程需要从用户态切换成内核态的时候,会修改处理器的特权等级,从 3(用户态)改成 0(内核态),这里的处理器包括(CR0,CR3,CR4寄存器),当从用户态转化成内核态的时候,会保存用户态的上下文信息,并加载内核态的上下文信息,然后就可以访问内核态的页表,执行操作系统的内置代码,比如进程调度,异常处理等操作,当工作完成后,就会拿着计算结果,恢复用户态的上下文数据,再返回给用户,然后回到刚刚中断的代码后继续执行

总之,在程序的运行过程中,操作系统无形中会大量地访问系统硬软件资源,在这些访问过程中,操作系统都会切换成内核态,使用内核页表,然后调用内核部分的代码,最终再拿着计算的结果切换成用户态返回给用户

2.2 信号的监测和处理

所以当一个进程收到操作系统发送的信号之后,首先做的就是将 pending 中将对应的信号有效位置为 1,而该信号的处理一般是等到进程下一次嵌入内核态,再从内核态切换回用户态之前,因为在这之前,操作系统大都会检查该进程的信号状态。

  • 假设当前有个进程收到了信号 2,那么pending 上的相应位置也会被置为 1
  • 然后该进程突然执行了 write 等系统调用,就需要切换成内核态来执行操作系统的代码,以及访问硬件资源。然后按理来说,执行完 write 的时候,就应该拿着执行结果切回用户态返回给程序了
  • 但是在这之前,操作系统会以内核态的身份检查该进程的 pendingblock,如果 block中某信号标志位为 1,那么说明被屏蔽了,不用管它,而如果 block = 0, pending = 1 ,说明这个信号现在需要被处理
  • 如果信号的处理方法是 SIG_IGN,那么就忽略,将结果返回给用户,并从中断处继续往下执行
  • 如果信号的处理方法是 SIG_DEL,那么操作系统还是在内核态执行处理方法,比如终止进程…
  • 如果该信号的处理方法是用户自定义的函数,那么要执行这个函数,就又需要切换成用户态来执行这个处理函数(如果代码是恶意代码,那么以内核态的身份执行就存在风险)
  • 处理完这个函数之后,也不能直接将结果从用户层返回,因为还需要继续回到内核态,执行内核代码,比如更新进程的状态数据,在内核态中执行信号处理程序的收尾工作等
  • 最后再拿着执行结果返回给用户,从系统调用 write 处继续往后执行
  • 就完成了一次信号的处理

在这里插入图片描述

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

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

相关文章

MapstructPlus的快速集成

https://www.mapstruct.plus/https://www.mapstruct.plus/ # 博主技术栈如下 springboot:2.4.5 lombok:1.8.20 mapstruct-plus:1.3.4 knife4j:4.0.0目录 一、添加依赖(谨防依赖冲突) 二、如果依赖下不下来,要在maven的setting文件中加入腾讯…

Vue面试题整理

vue修饰符&适用场景 vue修饰符大致分为5类:表单修饰符,事件修饰符,鼠标修饰符,键盘修饰符,v-bind修饰符 路由守卫 全局路由守卫:beforeEach(全局前置守卫),afterEa…

wsl子系统Ubuntu18.04,cuDNN安装

如果觉得本篇文章对您的学习起到帮助作用,请 点赞 关注 评论 ,留下您的足迹💪💪💪 本文主要wls子系统Ubuntu18.04安装cuDNN,安装cudnn坑巨多,因此记录以备日后查看,同时&#xff0…

PC市场寒冬,大众还需要PC吗?

PC市场寒冬,大众还需要PC吗? PC(个人电脑)市场从2016年智能手机兴起之时便进入下滑态势,到2020年疫情发生后,居家办公、在线教育等需求曾给PC市场带来连续六个季度的增长。⁴ 好景不长,进入202…

A Survey on Multimodal Large Language Models

本文是LLM系列的文章之一,主要是讲解多模态的LLM。针对《A Survey on Multimodal Large Language Models》的翻译。 多模态大语言模型的综述 摘要1 引言2 概述3 方法3.1 多模态指令调整3.1.1 引言3.1.2 前言3.1.3 模态对齐3.1.4 数据3.1.5 模态桥接3.1.6 评估 3.2 …

Android Studio实现内容丰富的安卓美食管理发布平台

如需源码可以添加q-------3290510686,也有演示视频演示具体功能,源码不免费,尊重创作,尊重劳动。 项目编号079 1.开发环境 android stuido jdk1.8 eclipse mysql tomcat 2.功能介绍 安卓端: 1.注册登录 2.查看公告 3.查…

【网络进阶】Posix API与网络协议栈(三)

文章目录 1. 网络攻击和POSIX API与网络协议栈的关系1.1 网络攻击的基本概念和它们对协议栈的影响1.2 分布式拒绝服务(DDoS)攻击和网络协议栈1.3 地址解析协议(ARP)欺骗和POSIX API 2. 网络协议栈的理解和划分2.1 OSI七层模型2.2 …

PostgreSQL使用localhost可以连接,使用IP无法连接

问题描述:PostgreSQL使用localhost可以连接,使用IP无法连接 默认情况下,刚安装完成的 postgresSQL12 无法使用 数据库连接工具(如postman)连接。需要为其修改配置,开放连接权限。 修改pg_hba.conf 增加…

B063-基础环境-后端环境 Lombok Restful postman swagger

目录 项目介绍需求分析项目架构项目技术架构前后端分离前后端分离开发模式前后端分离优缺点 后端项目搭建-部门模块项目端口规划创建SpringBoot项目流程整合SSM引用属性传值 LombokRestful风格Postman接口测试swagger接口规范其他 项目介绍 本项目是一个(Online To…

蓝奥声开发高性价比智能wifi插座进军智能家居

关键词:智能家居、家用插座、WiFi插座、高性价比插座 智能硬件的大潮袭来让智能家居这一并不新鲜的概念再次火热起来,关于智能家居的各种场景的描述给了我们很大的想象空间,然而落到实处真正开始走进生活时却又显得那么骨感,一时间…

使用EXCEL公式编程从网页获取股票数据并保存到excel

共享文件地址 在dataexcel服务器的 /示例/03函数示例/获取股票数据8.fexm 路径 公式脚本 var urlcelltext("URL");//获取单元格ID为URL的单元格内容 var txtWebGet(url); //从网页获取数据 cellvalue("data",txt); //将获取到的数据库保存到ID为DATA的单…

JMeter从数据库中获取数据并作为变量使用

目录 前言: 1、JMeter连接MySQL数据库 2、线程组下新建一个 JDBC Connection Configuration 配置元件 3、实现数据库的查询-单值引用 4、实现数据库的查询-多值引用 前言: 在进行接口测试时,经常需要使用一些动态的数据,例如…