linux信号 | 学习信号四步走 | 透析信号是如何被处理的?

news2025/5/26 0:33:04

        前言:本节内容讲述linux信号的捕捉。 我们通过前面的学习, 已经学习了信号的概念, 信号的产生, 信号的保存。 只剩下信号的处理。 而信号的处理我们应该着重注意的是第三种处理方式——信号的捕捉。 也就是说, 这篇文章其实最核心的部分就是在解释信号的捕捉。 但是大的篇幅还是在前面的预备知识上面。现在, 开始我们的学习吧!

        ps: 本节内容适合了解信号概念, 信号产生, 信号保存的友友们观看。

目录

信号是什么时候被处理的呢? 

重谈地址空间

操作系统的执行逻辑

cpu与用户态和内核态

捕捉过程


信号是什么时候被处理的呢? 

        我们要处理一个信号, 首先是我们自己知道自己收到信号了。 那么知道自己收到信号了, 进程就必须得在合适的时候查一查我们对应的pending位图, block位图以及handler数组。 而这三个表都属于内核数据结构 进程不会, 也不能直接访问他们。  而解决方法是让进程处于一种内核状态。 当进程从内核态转回用户态的时候就会对信号做检测并处理

        关于内核态用户态, 是我们从学习linux到现在, 第一次听到的名词。一般我们自己进程在进行的时候, cpu进行调度的时候不仅仅运行我们自己写的代码。 比如我们的代码里还有系统调用或者库函数调用。 对应着就是我们自己写的代码, 库自己写的代码, 操作系统曾经写的代码。 ——这些cpu都要跑。 就比如我们进程等待wait函数, 我们创建子进程的fork, 我们打印数据printf。 这些操作我们都没有自己进行, 都是通过系统调用或者库函数直接进行调用。 

        那么, 我们的操作系统既然不相信我们的用户, 所以在很多场景下, 它是需要我们的用户做一下相关的身份切换的, 才允许我们执行对应的代码。 一般我们在执行库函数的时候, 执行我们自己的代码的时候, 一般都是在用户态直接执行的还有一种是进程在cpu调度之下, 要陷入到操作系统内部, 执行对应的任务——最典型的就是在调用系统调用的时候。——这里要知道的是, 当我们调用系统调用的时候, 操作系统会自动把我们的用户的身份进行转变, 转变为内核身份。 然后由操作系统帮我们把函数执行完毕。 当返回时, 再把我们的身份从内核身份转化为用户身份。 此时才能够允许我们执行系统调用。 ——这里我们只需要知道, 系统调用除了调用函数, 也是需要进行身份变换的。 这里的身份就两个, 一个是用户态, 一个是内核态。 
        也就是说, 调用系统调用, 操作系统是自动会做身份切换的。 (从用户到内核, 或者从内核到用户)

重谈地址空间

        我们其实之前学到的进程相关的知识, 其实都是和我们上图中的用户空间相关联(0~3GB), 但是在3~4GB还有一个内核地址空间这个内核地址空间其实映射的就是操作系统的代码和数据。 而且因为操作系统最先被加载进进程的程序, 所以它的代码和数据应该在内存中的较为底部的位置。 同时映射的时候需要有一个内核级页表进行映射。 

        那么这里问题就来了, 如果我们的系统中有很多进程的话, 用户页表有几份呢 ? 内核级页表有几份呢 ?

  •         对于用户级页表: 有几个进程就有几份用户级页表, 因为进程具有独立性。 
  •         对于内核级页表: 有一份。

        所以, 我们的每一个进程, 看到的都是3 ~ 4GB内核空间里面的内容, 看到的内核级页表里面的内容, 看到的物理内存里面的操作系统的代码和数据的内容都是一样的!!

        要知道, 我们的各种系统调用的方法, 比如write, read, wait, fork, sigset_t等等都是在内核空间的。 

        所以, 以后我们进程在代码区进行系统调用的时候, 就直接找自己内核地址空间里面的系统方法然后返回到代码区

        就如同在自己的地址空间里面直接调用

  • ——在进程的视角: 我们调用系统当中的方法, 就是在我们自己的地址空间中进行执行的。        
  • ——在OS的视角: 在任何一个时刻, 都有进程在执行。 我们想执行操作系统的代码, 就可以随时执行。 

操作系统的执行逻辑

        执行逻辑就是——基于时钟中断的一个死循环。 

        在我们计算机里面都有一个芯片单元, 每隔很短很短的时间, 向计算机发送时钟中断 接收到所谓的时钟中断后, 就要执行中断所对应的方法, 也就是操作系统的代码。 也就是说我们的计算机内部有一个芯片单元, 它每隔很短的时间就给cpu发送一次时钟中断, 这个就叫做一次滴答。 然后我们操作系统, 就被动的被时钟中断推着向后执行。 

        然后操作系统将前期的工作做完之后, 往后的过程中就是执行一个死循环, 在死循环中检测有没有时钟到来, 如果有时钟到来, cpu就去执行时钟中断对应的方法。具体的操作系统如何做的, 看下图:

        

这张图里面有些字段已经被博主标记出来了, 但是有些字段涉及的内容太多, 无法全部截图, 所以这里直接叙述:

        首先说这个for(; ;) pause(); 这个是一个死循环,说明操作系统执行到这里后就什么都没干, 一直执行一个pause()pause的意思是暂停, 为什么暂停? 因为操作系统往后的动作, 都靠着时钟中断来驱动, 也就是说, 有外设中断, 操作系统就去执行对应的中断。 

        也就是说, 操作系统最后会卡在for(; ;) pause();  什么都不去做, 一直暂停在这里相应中断。 而我们的操作系统响应中断, 就一直在执行相应的调用。 所以, 我们的硬件一直在推着操作系统在走。 所以, 我们的操作系统才一直推着我们的进程在走。 所以我们的代码才得以推进。 

        ps:我们的计算机天然就有计算时间的能力, 就比如我们的台式电脑即便断电, 再次开机的时间也能够正确。 这是因为计算机内部可以续电, 可能是纽扣电池, 然后我们计算机能够通过一种硬件单元进行时间的计算。 

        然后要着重说一下的就是这个调度程序初始化:

        这个timer_interrupt, 如果没有中断就绪, 就会执行这个。 这个东西是用来相应每隔很小时间发来的中断的, 比如执行或者调度等操作。 

cpu与用户态和内核态

        我们说过, 么一个当前正在调度的进程, 对应的这个进程有自己的页表。 并且, cpu中有一个CR3寄存器, 这个寄存器直接指向当前进程所对应的用户级页表。 

        同时有一个ECS寄存器。 我们怎么知道我们当前是用户态还是内核态呢? 进程如果在执行用户的代码的时候, ECS一定是指向用户态进程如果在执行系统的代码的时候, ECS一定是指向内核态。 关键在于, 在ECS寄存器里面, 最后有着两个比特位。 这两个比特位有四种表示方式:00、01、10、11. linux内核中, 对于cpu常见的有两种工作模式, 一种叫做内核态00, 一种叫做用户态01。 所以如果今天想访问内核态对应的代码, 必须想办法将ECS的低两位由三置为0. 那么谁能做到呢? ——cpu必须给我们提供一个方法, 能够修改自己的工作状态, 即int 80陷入内核。

  •         内核态: 允许访问操作系统的代码和数据。 
  •         用户态: 只能访问用户自己的代码和数据。

捕捉过程

要理解信号的捕捉过程, 我们可以先看下面这张图。     接下来就是对这张图进行解释:

  •         首先, 信号的处理必须是操作系统要先检测到信号。 那么这个检测的时机就是上面图中的红圈圈。 上面这张图是一个循环的, 但是进程的开始要从绿色的位置执行。 然后一开始在用户态, 在用户态执行main函数的时候, 遇到异常或者其他触发信号的调用, 就进入内核态。 也就是经过第一个蓝色圈圈, 到了下层。 然后操作系统就进行检测。 
  •         现在谈一谈这个检测:做检测, 首先要遍历pending列表。 看看有没有信号,再看看有没有block。 没有就正常执行, 有的话就不做处理, 继续看下一个。 直到遇到1, 并且block没有被阻塞就执行信号动作。那么此时如果这个信号是DFL,就是默认动作, 该怎么样就怎么样。 如果是IGN, 就是忽略, 什么都不做。 并且在执行这个信号动作之前将信号的pending由零变成1。 这两种都是直接在内核态执行的。
  •         但是问题来了, 如果不是忽略或者默认, 而是自定义动作呢? ——要知道, 我们的自定义动作是用户代码,执行它就需要进入用户态。 那么我们为什么要使用用户态而不直接使用内核态呢? 因为如果我们调用的是非法代码。 盗取了我们计算机内的重要信息, 此时就会出现危险。 所以我们必须返回用户态。 然后从用户态处理完之后, 就会原路返回, 也就是利用sigreturn系统调用回归内核态, 再利用sys_sigreturn返回main。 
  •         那么进程我们知道是会被调度的, 只要他再跑, cpu就会调度它的进程。 只要需要调度, 时间片必然会消耗完毕。 消耗完毕就必然将时间片从cpu上剥离下来, 当下次调用进程的时候, 必然要把进程的各种队列放到寄存器里面跑, 而这个过程中, 见数据恢复到cpu中的过程一定在内核态, 而开始执行进程代码时一定是在用户态。 所以, 在进程调度的时候, 有无数次机会从进程用户态到内核态, 从内核态到用户态。 

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

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

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

相关文章

基于yolov8的100种蝴蝶智能识别系统python源码+pt模型+训练日志+精美GUI界面

【算法介绍】 基于YOLOv8的100种蝴蝶智能识别系统是一个结合了深度学习和人工智能技术的先进工具,旨在提高生物多样性监测和保护领域的效率和精确度。该系统利用YOLOv8深度学习算法,通过9955张图片的训练,能够准确识别100种不同的蝴蝶类型&a…

15分钟学 Python 第37天 :Python 爬虫入门(三)

Day 37 : Python爬虫入门大纲 章节1:Python爬虫概述 1.1 什么是爬虫? 网页爬虫(Web Crawler)是一种自动访问互联网上网页并提取数据的程序。爬虫的作用包括搜索引擎索引内容、市场调查、数据分析等。 1.2 爬虫的工作原理 发起…

深入探究:在双链表指定元素的后面进行插入操作的顺序

归纳编程学习的感悟, 记录奋斗路上的点滴, 希望能帮到一样刻苦的你! 如有不足欢迎指正! 共同学习交流! 🌎欢迎各位→点赞 👍 收藏⭐ 留言​📝惟有主动付出,才有丰富的果…

Redis篇(缓存机制 - 分布式缓存)(持续更新迭代)

目录 一、单点 Redis 的问题 1. 数据丢失问题 2. 并发能力问题 3. 故障恢复问题 4. 存储能力问题 5. 四种问题的解决方案 二、Redis持久化(两种方案) 1. RDB持久化 1.1. 简介 1.2. 执行时机 save命令 bgsave命令 停机时 触发RDB条件 1.3. …

SpringMVC项目的创建和使用

1.新建module,名称叫02_springmvc   2.新建文件夹web   3.点击确定,就会看到如下图,idea自动给我们创建了web.xml   这时候web文件夹多一个小点点的标识 &am…

OS_过程调用与系统调用

2024.06.25:操作系统过程调用与系统调用学习笔记 第5节 过程调用与系统调用 5.1 过程调用/函数调用/子程序调用5.2 系统调用5.2.1 系统调用汇编层 5.3 过程调用与系统调用的对比 5.1 过程调用/函数调用/子程序调用 (过程调用)也称为&#xf…

SpringBoot框架下校园资料库的构建与优化

1系统概述 1.1 研究背景 如今互联网高速发展,网络遍布全球,通过互联网发布的消息能快而方便的传播到世界每个角落,并且互联网上能传播的信息也很广,比如文字、图片、声音、视频等。从而,这种种好处使得互联网成了信息传…

【Conda】修复 Anaconda 安装并保留虚拟环境的详细指南

目录 流程图示1. 下载 Anaconda 安装程序2. 重命名现有的 Anaconda 安装目录Windows 操作系统Linux 操作系统 3. 运行新的 Anaconda 安装程序Windows 操作系统Linux 操作系统 4. 同步原环境使用 robocopy 命令(Windows)使用 rsync 命令(Linux…

CUDA与TensorRT学习四:模型部署基础知识、模型部署的几大误区、模型量化、模型剪枝、层融合

文章目录 一、模型部署基础知识1)FLOPS和TOPS定义介绍、计算公式(1)基础定义(2)计算公式(3)FLOPS在GPU试怎么运算?(4)Ampere SM的电子元件结构 2)…

【小沐学GIS】blender导入OpenTopography地形数据(BlenderGIS、OSM、Python)

文章目录 1、简介1.1 blender1.2 OpenStreetMap地图 2、BlenderGIS2.1 下载BlenderGIS2.2 安装BlenderGIS2.3 申请opentopography的key2.4 抓取卫星地图2.5 生成高度图2.6 获取OSM数据 结语 1、简介 1.1 blender https://www.blender.org/ Blender 是一款免费的开源 3D 创作套…

[uni-app]小兔鲜-07订单+支付

订单模块 基本信息渲染 import type { OrderState } from /services/constants import type { AddressItem } from ./address import type { PageParams } from /types/global/** 获取预付订单 返回信息 */ export type OrderPreResult {/** 商品集合 [ 商品信息 ] */goods: …

微信小程序地理定位与逆地址解析详解

地理定位 1 原理与思路 在微信小程序中,地理定位功能可以通过调用微信提供的API接口来实现。这些接口允许我们获取用户的当前位置或者让用户通过地图选择位置。获取到位置信息后,我们可以使用逆地址解析来获取详细的地址信息,如省、市、区、…

CUDA安装教程

文章目录 一、CUDA的下载和安装1.1 查看NVIDIA适配CUDA版本1.2 下载CUDA Toolkit1.3 安装CUDA 二、环境配置三、查看是否安装成功 一、CUDA的下载和安装 CUDA在深度学习中允许开发者充分利用NVIDIA GPU的强大计算能力来加速深度学习模型的训练和推理过程。 1.1 查看NVIDIA适配…

15分钟学 Python 第39天:Python 爬虫入门(五)

Day 39:Python 爬虫入门数据存储概述 在进行网页爬虫时,抓取到的数据需要存储以供后续分析和使用。常见的存储方式包括但不限于: 文件存储(如文本文件、CSV、JSON)数据库存储(如SQLite、MySQL、MongoDB&a…

多模态理论基础——什么是多模态?

文章目录 多模态理论1.什么是多模态(multimodal)2.深度学习中的多模态 多模态理论 1.什么是多模态(multimodal) 模态指的是数据或者信息的表现形式,如文本、图像、音频、视频等 多模态指的是数据或者信息的多种表现…

算法笔记(十)——队列+宽搜

文章目录 N 叉数的层序遍历二叉树的锯齿形层序遍历二叉树最大宽度在每个树行中找最大值 BFS是图上最基础、最重要的搜索算法之一; 每次都尝试访问同一层的节点如果同一层都访问完了,再访问下一层 BFS基本框架 void bfs(起始点) {将起始点放入队列中;标记…

一款基于.NET开发的简易高效的文件转换器

前言 今天大姚给大家分享一款基于.NET开发的免费(GPL-3.0 license)、简易、高效的文件转换器,允许用户通过Windows资源管理器的上下文菜单来转换和压缩一个或多个文件:FileConverter。 使用技术栈 ffmpeg:作为文件转换…

vite学习教程03、vite+vue2打包配置

文章目录 前言一、修改vite.config.js二、配置文件资源/路径提示三、测试打包参考文章资料获取 前言 博主介绍:✌目前全网粉丝3W,csdn博客专家、Java领域优质创作者,博客之星、阿里云平台优质作者、专注于Java后端技术领域。 涵盖技术内容&…

Python | Leetcode Python题解之第457题环形数组是否存在循环

题目: 题解: class Solution:def circularArrayLoop(self, nums: List[int]) -> bool:n len(nums)def next(cur: int) -> int:return (cur nums[cur]) % n # 保证返回值在 [0,n) 中for i, num in enumerate(nums):if num 0:continueslow, fas…

Qt中使用QPainter绘制阴影

困扰了很久的问题,今天终于明白了如何绘制QGraphicDropShadowEffect同样效果的阴影,故写下这篇文章分享给大家。其方法是复制Qt源代码中QGraphicDropShadowEffect绘制实现的核心代码然后稍作修改实现,先看效果和封装过后的源代码:…