[Linux] 信号保存与处理

news2024/12/21 9:21:23

🪐🪐🪐欢迎来到程序员餐厅💫💫💫

          主厨:邪王真眼

主厨的主页:Chef‘s blog  

所属专栏:青果大战linux

总有光环在陨落,总有新星在闪烁


信号的保存

下面的概念务必记住

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

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

  3. 进程可以选择阻塞 (Block )某个信号。

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

  5. 阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作

信号的内核描述

如图所示,我们的task_struct中保存了三张表,block表,pend表,handler表

pending表

它本质就是一个位图

 handler表

它本质就是一个函数指针数组

 typedef void (*sighandler_t)(int);

我们之前通过signal函数给指定信号设置自定义方法,本质就是把signal函数的sighandler参数写入handler表,所以我们signal调用一次,就可以永久修改一个进程的信号处理方法

 block表

它本质也是一个位图

被阻塞的信号,即是被写入pending表,也不会被处理,直到阻塞被解除才会去处理它

有了这三张表,我们就可以直到一个信号有没有被传进来,他有没有被阻塞,如果没被阻塞那他又该如何处理,如此一来我们的进程就可以识别信号了,而三张表的实现,当然是当初写OS的程序员通过代码实现的,因此我们说进程认识信号,是程序员内置的结果。


内核数据结构

我们需要先了解一些类型,如下所示,不懂的看注释

struct task_struct {
struct sighand_struct *sighand;//hand表
sigset_t blocked//block表
struct sigpending pending;//pend表
...
}

先看hand表的结构体类型

struct sighand_struct {
atomic_t count;
struct k_sigaction action[_NSIG]; //  _NSIG是个宏,为64
spinlock_t siglock;
};
//上面的结构体中包含了K_sigaction这个结构体,它的定义就在下面
struct k_sigaction {
struct __new_sigaction sa;
void __user *ka_restorer;
};
//上面的结构体中包含了__new_sigaction这个结构体,它的定义就在下面
struct __new_sigaction {
__sighandler_t sa_handler;
unsigned long sa_flags;
void (*sa_restorer)(void); /* Not used by Linux/SPARC */
__new_sigset_t sa_mask;
};
//上面的结构体中包含了__sighandler_t这个类型,它的定义就在下面
typedef void (*__sighandler_t)(int);//即函数指针

再看pend表

struct sigpending {
struct list_head list;//表示这是个链表结构
sigset_t signal;//它的重点是这个sigset_t类型
};

 显然,pend表的内容是sigset_t类型的变量存储的,block表也是,sigset_t被称为信号集,这个类型定义如下

typedef struct
{
  unsigned long int __val[_SIGSET_NWORDS];
} __sigset_t;//注意这前面有两个下划线


typedef __sigset_t sigset_t;//这次typedef后,前面没有下划线了
这个sigset_t类型本质就是个unsigned long类型的数组,这个数组就是用来当位图用的
OS还很贴心的准备了一组接口用于操作该类型的变量
#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(const sigset_t *set, int signo);
  1.  sigemptyset:使信号集set中的所有比特位变为0

  2. sigfillset:使信号集set中的所有比特位变为1

  3. sigaddset:使信号集set的第signum位变为1

  4. sigdelset:使信号集set的第signum位变为0

  5. sigismember:检测信号集set的第signum位是0还是1 

  • 在使用sigset_ t类型的变量前,⼀定要调用sigemptyset或sigfillset做初始化,使信号集处于确定的状态。初始化sigset_t变量之后就可以在调用sigaddset和sigdelset在该信号集中添加或删除某种有效信号。
  • 这四个函数都是成功返回0,出错返回-1。
  • sigismember是⼀个布尔函数,用于判断⼀个信号集的有效信号中是否包含某种 信号,若包含则返回1,不包含则返回0,出错返回-1。

 sigprocmask

调⽤函数 sigprocmask 可以读取或更改进程的block表。
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
返回值:若成功则为0,若出错则为-1
  • how参数
    • 这个参数决定了如何修改信号屏蔽字,它有以下几种取值:
      • SIG_BLOCK:将set参数所指向的信号集添加到当前信号屏蔽字中。
      • SIG_UNBLOCK:从当前信号屏蔽字中移除set参数所指向的信号集中的信号。
      • SIG_SETMASK:将当前信号屏蔽字设置为set参数所指向的信号集。这会完全替换当前的信号屏蔽状态。
  • set参数
    • 这是一个指向sigset_t类型的信号集的指针。
  • oldset参数
    • 这是一个指向sigset_t类型信号集的指针,用于保存本次修改前的信号屏蔽字状态。如果不需要保存旧状态,可以将此参数设置为NULL

9号信号(SIGKIILL和19号信号(SIGSTOP)不能block


sigpending

#include <signal.h>
int sigpending(sigset_t *set);
读取当前进程的未决信号集,通过set参数传出。
调⽤成功则返回0,出错则返回-1

为什么有可以修改block表的函数,但是没有修改pend表的函数

因为不需要,我们上节课学习的信号产生的方式都是在修改pend,有他们就够了


实操函数

#include<iostream>
#include<unistd.h>
#include<signal.h>
#include<sys/types.h>

int main(){
    //对二号信号进行屏蔽
    sigset_t ne;
    sigset_t old;
    sigemptyset(&ne);
    sigemptyset(&old);
    sigaddset(&ne,2);//此时还没有把2号加到内核的block中,只是放到了栈上的ne中了
    sigprocmask(SIG_BLOCK,&ne,&old);//现在加到内核了
    int n=7;
    while(true){
        n--;
        if(n==0)
        sigprocmask(SIG_SETMASK,&old,nullptr);
        sigset_t pe;
        sigemptyset(&pe);
        sigpending(&pe);
        for(int i=31;i;i--)
        std::cout<<sigismember(&pe,i);
        std::cout<<std::endl;
        sleep(1);
    }

}

我们输入ctrl+c即二号命令,但是进程没有立即终止,是因为我们把二号信号写进block表了 

7秒之后,block表置零,二号信号不再被block于是被处理了,所以进程终止。


硬件中断

外设资源是否准备好,不能让OS去轮询检测,因为外设非常多,效率会很低,太浪费资源了,这里采用的解决方案就是硬件中断。

硬件中断是指由计算机硬件设备(如键盘、鼠标、硬盘、网卡等)发出的信号,用于通知 CPU暂停当前正在执行的程序,转而处理与该硬件设备相关的特定事件这使得 CPU 能够及时响应硬件设备的请求或状态变化,从而实现设备与 CPU 之间的高效交互。这些东西的具体实现是依靠硬件电路实现的,我们不用管。

  

但是外设实在是太多了,所以不能直接连接到cpu的针脚上,于是就设计了一个中断控制器来负责连接他们,每个设备都被编好了中断号,中断控制器通过中断号知晓是哪个设备发来的中断,然后会把该信息发给CPU。CPU会告知OS,OS会根据中断号去中断向量表执行对应的方法(例如去键盘获取信息)。

这里的通知CPU就是其实向着CPU的特定针脚发送高电平信号

程序员在写OS时,就提前给每一种设备准备好处理中断的方案,这些方案本质就是函数,他们汇聚在一起就是一个中断向量表,我们把它当作一个函数指针数组即可。访问该数组元素的下标就是中断号。

当然,如果硬件中断信息告知给CPU时,CPU要开始执行中断处理方法,就不能继续跑之前的进程,为了保证之后还能正常运行它,需要进行CPU现场保护

 CPU 现场保护是指在计算机系统发生中断或异常情况时,CPU 暂停当前正在执行的程序,为了能够在后续恢复该程序的执行,将当前程序执行的状态信息进行保存的过程。这些信息包括程序计数器(PC)的值、通用寄存器的内容、状态寄存器(也称为程序状态字 PSW)的内容等。这个和进程切换时的上下文数据保存有些像,但请注意这俩不是一个东西。 


 时钟中断

进程是在OS在管理控制下运行的,那么OS又是被谁指挥的呢?

时钟源是一个定期发送触发硬件中断信号的硬件,他的中断号对应的中断处理方式就是进程调度

所以OS才可以不断的调度进程,为了提高效率,这个时钟源已经被集成到CPU内部了,他的中断发送不依赖中断控制器,是直接发送给CPU的。我们计算机中有个参数叫做主频,表示时钟源一秒发送多少中断单位一般是GHZ,所以主频快的话,OS响应速度就比较快,效率就高了

于是OS就可以开摆了,OS所谓的调度进程,就是时钟源定时发送中断,然后OS去根据设置好的处理方法处理中断;OS要和外设IO,就是等硬件发送中断,然后它根据中断向量表去调用对应的函数。

无端联想,OS就像一个商店老板,他的店里什么都有,当用户需要一样东西时,OS虽然不知道这个东西是干嘛的,是怎么造出来的,但是他知道他的店里有,而且确切的直到放到哪里了,于是它只需要去把这个东西拿给用户就好。

因此操作系统再初始化完需要的资源后,就直接进入死循环,等待中断信号的到来,然后执行对应的方法即可。


时间片

时钟中断是定时发送的,我们假设它一纳米发送一次,

我们可以给进程task_struct设置一个变量——时间片

int time_piece=1000;

那么时间片的大小就是1000*1纳秒

每次时钟源发送信号,OS进行调度都会把当前进程的时间片减一

当减到零,就进行进程切换,否则继续执行该进程


软中断

有没有可能,上面这一套中断的流程,靠软件也可以实现呢

为了让操作系统支持进行系统调用,CPU也设计了对应的汇编指令(int 或者 syscall),可以让CPU内

部触发上面的中断逻辑。

把这些汇编写在系统接口函数里,不就可以让OS支持系统调用了吗

这里我们以int指令为例,他的中断号是ox80

  

它对应的中断向量表中的元素也是一个函数我们暂时称他为func(),我们的OS要是实现很多系统接口,这些接口会放到一个数组中,被称为系统调用表,所以要使用某个系统调用,只要使用他在表中下标即可。现在我们要使用系统调用,只需要把系统调用号传到func()中即可。 

第一个问题

用户层怎么把系统调用号给操作系统?

把系统调用号写进CPU寄存器,之后查看寄存器即可,只要提前设计好OS去哪个寄存器查看即可,系统接口的蚕食也是依靠寄存器传给OS的。

第二个问题

操作系统怎么把返回值给用户?

把最后return的值放到一个寄存器中,再把该值move到用户用于接受返回值的变量中。

系统调用的过程,其实就是先int 0x80、syscall触发软中断,并且把系统调用号(以及形参)写到一个寄存器里,然后CPU告知OS,OS根据寄存器的值去系统调用表执行对应的函数。

现在我们知道了使用系统接口,不一定就要用系统函数,你可以先通过int汇编代码触发ox80号中断,然后把要用的系统调用接口的编号和参数通过汇编move到指定的寄存器,最后OS会找到要执行的函数,并且运行它。

事实上,这才是OS提供的真正的系统接口,而不是我们用的那些c语言函数(write、read、fork这些)。我们所用的fork这些接口,是c语言封装过的函数,毕竟让用户用真的系统实在是太难了。

这样一来既方便了用户,也保护了OS,毕竟如果你用int(0x80),万一传入的系统调用号非法呢,或者你用了一些别的奇葩操作,都会危害到OS,现在只让你用c语言封装的接口,你就害不了他了

左64位,右32位真系统接口

   

c语言才是世界上最好的语言!

缺页中断、除零错误、野指针,他们本质都是被转化为软中断,OS会提前给每种情况设置处理方法,也都有自己的中断号。

  • CPU内部的软中断,比如int 0x80或者syscall,他们不是说出现的什么错误,知识单纯让进程陷入内核去进行系统调用的,我们把这种操作叫做陷阱(这个名字听起来有点怪)

  • CPU内部的软中断,比如除零/野指针等,我们叫做异常。

所以,能理解“缺页异常” 为什么这么叫了吗?

OS是什么

它就是躺在中断处理例程上的代码块

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

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

相关文章

计算机网络-GRE Over IPSec实验

一、概述 前情回顾&#xff1a;上次基于IPsec VPN的主模式进行了基础实验&#xff0c;但是很多高级特性没有涉及&#xff0c;如ike v2、不同传输模式、DPD检测、路由方式引入路由、野蛮模式等等&#xff0c;以后继续学习吧。 前面我们已经学习了GRE可以基于隧道口实现分支互联&…

进网许可认证、交换路由设备检测项目更新25年1月起

实施时间 2025年1月1日起实施 涉及设备范围 核心路由器、边缘路由器、以太网交换机、三层交换机、宽带网络接入服务器&#xff08;BNAS&#xff09; 新增检测依据 GBT41266-2022网络关键设备安全检测方法交换机设备 GBT41267-2022网络关键设备安全技术要求交换机设备 GB/…

用C#(.NET8)开发一个NTP(SNTP)服务

完整源码&#xff0c;附工程下载&#xff0c;工程其实也就下面两个代码。 想在不能上网的服务器局域网中部署一个时间服务NTP&#xff0c;当然系统自带该服务&#xff0c;可以开启&#xff0c;本文只是分享一下该协议报文和能跑的源码。网上作为服务的源码不太常见&#xff0c;…

Connection lease request time out 问题分析

Connection lease request time out 问题分析 问题背景 使用apache的HttpClient&#xff0c;我们知道可以通过setConnectionRequestTimeout()配置从连接池获取链接的超时时间&#xff0c;而Connection lease request time out正是从连接池获取链接超时的报错&#xff0c;这通常…

【课程论文系列实战】:随机对照实验驱动的电商落地页优化

数据与代码见文末 摘要 随机对照试验&#xff08;Randomized Controlled Trial&#xff0c;RCT&#xff09;被认为是因果推断的“金标准”方法。通过随机分配实验参与者至不同组别&#xff0c;确保了组间可比性&#xff0c;RCT能够有效地消除样本选择偏差和混杂变量问题。本文…

UML 建模实验

文章目录 实验一 用例图一、安装并熟悉软件EnterpriseArchitect16二、用例图建模 实验二 类图、包图、对象图类图第一题第二题 包图对象图第一题第二题 实验三 顺序图、通信图顺序图银行系统学生指纹考勤系统饮料自动销售系统“买到饮料”“饮料已售完”“无法找零”完整版 通信…

高质量翻译如何影响软件用户体验 (UX)

在软件开发领域&#xff0c;用户体验 (UX) 是决定产品成败的关键因素之一。一个流畅、吸引人且直观的用户体验可以决定一款软件的成功与否。在影响优秀用户体验的众多因素中&#xff0c;高质量翻译尤为重要&#xff0c;尤其是在当今全球化的市场环境中。确保软件为不同语言和文…

ArcGIS Pro 3.4新功能2:Spatial Analyst新特性,密度、距离、水文、太阳能、表面、区域分析

Spatial Analyst 扩展模块在 ArcGIS Pro 3.4 中引入了新功能和增强功能。此版本为您提供了用于表面和区域分析的新工具以及改进的密度和距离分析功能&#xff0c;多种用于水文分析的工具性能的提高&#xff0c;一些新的太阳能分析功能。 目录 1.密度分析 2.距离分析 3.水文…

Linux C 程序 【05】异步写文件

1.开发背景 Linux 系统提供了各种外设的控制方式&#xff0c;其中包括文件的读写&#xff0c;存储文件的介质可以是 SSD 固态硬盘或者是 EMMC 等。 其中常用的写文件方式是同步写操作&#xff0c;但是如果是写大文件会对 CPU 造成比较大的负荷&#xff0c;采用异步写的方式比较…

凯酷全科技抖音电商服务的卓越践行者

在数字经济蓬勃发展的今天&#xff0c;电子商务已成为企业增长的新引擎。随着短视频平台的崛起&#xff0c;抖音作为全球领先的短视频社交平台&#xff0c;不仅改变了人们的娱乐方式&#xff0c;也为品牌和商家提供了全新的营销渠道。厦门凯酷全科技有限公司&#xff08;以下简…

精准提升:从94.5%到99.4%——目标检测调优全纪录

&#x1f680; 目标检测模型调优过程记录 在进行目标检测模型的训练过程中&#xff0c;我们面对了许多挑战与迭代。从初始模型的训练结果到最终的调优优化&#xff0c;每一步的实验和调整都有其独特的思路和收获。本文记录了我在优化目标检测模型的过程中进行的几次尝试&#…

STM8单片机学习笔记·GPIO的片上外设寄存器

目录 前言 IC基本定义 三极管基础知识 单片机引脚电路作用 STM8GPIO工作模式 GPIO外设寄存器 寄存器含义用法 CR1&#xff1a;Control Register 1 CR2&#xff1a;Control Register 2 ODR&#xff1a;Output Data Register IDR&#xff1a;Input Data Register 赋值…

国标GB28181平台EasyGBS在安防视频监控中的信号传输(电源/视频/音频)特性及差异

在现代安防视频监控系统中&#xff0c;国标GB28181协议作为公共安全视频监控联网系统的国家标准&#xff0c;该协议不仅规范了视频监控系统的信息传输、交换和控制技术要求&#xff0c;还为不同厂商设备之间的互联互通提供了统一的框架。EasyGBS平台基于GB28181协议&#xff0c…

如何使用checkBox组件实现复选框

文章目录 概念介绍使用方法示例代码我们在上一章回中介绍了DatePickerDialog Widget相关的内容,本章回中将介绍Checkbox Widget.闲话休提,让我们一起Talk Flutter吧。 概念介绍 我们在这里说的Checkbox也是叫复选框,没有选中时是一个正方形边框,边框内容是空白的,选中时会…

基于“2+1 链动模式商城小程序”的微商服务营销策略探究

摘要&#xff1a;本文探讨在竞争激烈的市场经济与移动互联网时代背景下&#xff0c;微商面临的机遇与挑战。着重分析“21 链动模式商城小程序”如何助力微商改变思路&#xff0c;通过重视服务、提升服务质量&#xff0c;以服务营销放大利润&#xff0c;实现从传统微商模式向更具…

1-1 STM32-0.96寸OLED显示与控制

1.0 模块原理图 2.0 0.96OLED简介 资料下载&#xff1a;https://jiangxiekeji.com/download.html 程序介绍&#xff1a;https://jiangxiekeji.com/tutorial/oled.html SSD1306是一款OLED/PLED点阵显示屏的控制器&#xff0c;可以嵌入在屏幕中&#xff0c;用于执行接收数据、显…

在Visual Studio 2022中配置C++计算机视觉库Opencv

本文主要介绍下载OpenCV库以及在Visual Studio 2022中配置、编译C计算机视觉库OpenCv的方法 1.Opencv库安装 ​ 首先&#xff0c;我们需要安装OpenCV库&#xff0c;作为一个开源库&#xff0c;我们可以直接在其官网下载Releases - OpenCV&#xff0c;如果官网下载过慢&#x…

QT:QDEBUG输出重定向和命令行参数QCommandLineParser

qInstallMessageHandler函数简介 QtMessageHandler qInstallMessageHandler(QtMessageHandler handler) qInstallMessageHandler 是 Qt 框架中的一个函数&#xff0c;用于安装一个全局的消息处理函数&#xff0c;以替代默认的消息输出机制。这个函数允许开发者自定义 Qt 应用…

网站灰度发布?Tomcat的8005、8009、8080三个端口的作用什么是CDNLVS、Nginx和Haproxy的优缺点服务器无法开机时

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c; 忍不住分享一下给大家。点击跳转到网站 学习总结 1、掌握 JAVA入门到进阶知识(持续写作中……&#xff09; 2、学会Oracle数据库入门到入土用法(创作中……&#xff09; 3、手把…

OpenGL ES 03 加载3张图片并做混合处理

OpenGL ES 02 加载3张图片并做混合处理 什么是纹理单元纹理单元的作用使用纹理单元的步骤详细解释加载图片并绑定到到GPU纹理单元采样器的设置1.设置采样器变量的纹理单元编号&#xff0c;目的是为了告诉纹理采样器&#xff0c;从哪个纹理单元采集数据2.如果你没有显式地设置采…