当前位置: 首页 > news >正文

Linux中的信号

一、信号介绍

kill -l 查看信号

1~31 普通信号,34~64 实时信号。

信号:Linux系统提供的一种向指定进程发送特定时间的方式,作识别处理。

信号的产生是异步的:进程间相互独立,不等待其他进程地执行。

信号的生命周期:信号的产生 -> 信号的保存 -> 信号的处理

二、信号处理

信号处理分类:默认处理,忽略处理,自定义处理(信号捕捉)

Action列记录信号处理默认操作。

1、默认处理

通常是停止,终止进程(Core, Term, Stop...),忽略。

2、自定义处理

捕捉信号进行处理 函数 signal()

signum:信号编号。

handler:处理信号的函数,返回值void, 参数信号编号。

3、举例

上述代码当OS发送2号信号给进程时,进程捕捉信号,不执行默认的2号信号操作(其实时 ctrl c 终止进程)而是打印 get a sig : 2

4、信号的发送

本质就是在进程的pcb中的信号位图 (uint32_t singals) 的指定信号编号位置的0变成1(1~31位)

由于修改的是内核数据结构,所以操作是由OS做的。

三、信号的五种产生方式

1、命令

kill -信号编号 pid    对指定进程发送信号

2、键盘输入

  ctrl c(信号2 SIGINT) ctrl \(信号3 SIGQUT)

3、系统调用

int kill(pid_t pid, int sig)   给任意进程发送信号

对系统调用的封装   int raise(int sig)      对当前进程发送信号(不常用)

语言级     void abort(void)     终止进程,给进程发送6号信号 SIGABRT

比较特殊,即使被捕捉,依然会终止进程,不像2,3号信号

4、软件条件

例如当管道文件读端关闭,写端一直在写入,OS就会给进程发送 SIGPIPE 信号终止进程。

闹钟函数会在进程开始后 second 秒发送闹钟信号。由于闹钟存在很多,就要先描述再组织,用小根堆维护,超时就发送信号 pop()掉。

返回值是上一个闹钟的剩余时间。

alarm(0)表示取消闹钟。

闹钟在一个进程中默认只触发一次,但是可以捕捉闹钟信号之后再设一个。

5、异常

当进程做了非法访问操作时,OS会给进程发送信号,默认终止进程,释放进程在CPU中的上下文数据。

可以捕获信号不终止进程,但是这样就一直报错。

问题

(1)CPU怎么知道异常?

以 10 / 0 为例,CPU中存在 eflag ,当10 / 0 使结果溢出之后 eflag 中的溢出标记位变成1,CPU发现异常。

(2)为什么推荐终止程序?

若不终止进程就会一直调度进程,每调度一次就会报错。

6、细节

对比core, term

term:单纯异常终止进程。

core:异常终止进程,返回一个 debug 文件,可以调式发现错误,默认云服务器不会产生(防止错误文件打满磁盘)叫做核心转储,记录异常数据。

在前文中我们提到进程等待函数 waitpid(),里面的输出型参数 status 前八位是退出码,后七位是退出信号,中间一位就是 core dump 标志,当标志为1时就表示进程异常退出,core文件写入。

调试方法:gdb 错误文件       core-file core 

四、信号保存

1、信号递达 Delivery

执行信号处理动作。

2、信号未决 Pending

信号从产生到递达的状态。

3、信号阻塞

进程可以阻塞信号,对应的信号就永远不会递达,一直未决,直到解除阻塞。

一个进程的阻塞与他是否未决无关。

在Linux中 sigset_t 是一个位图类型叫信号集,可以存储block, Pending位图

#include<signal.h>

位图清0       int sigempty(sigset_t* set)    

位图全置1   int sigfillset(sigset_t* set)

把信号设置进位图(在对应信号编号处置1)int sigaddset(sigset_t* set, int signo)

把信号删除(在对应信号编号处置0) int sigdelet(sigset_t* set, int signo)

判断信号是否在位图 int sigismember(sigset_t* set, int signo)

获取信号屏蔽字

int sigprocmask(int how, const sigset_t* set, sigset_t* oldset)

how:

SIG_BLOCK : 添加set中的屏蔽字,mask = mask | set

SIG_UBLOCK : 取消set中的屏蔽字,mask = mask &(~set)

SIG_SETMASK : 设置set内容,mask = set

set:输入型参数,传入指定信号屏蔽字的位图。

oldset:输出型参数,输出旧的信号屏蔽字。

获取Pending 

int sigpending(sigset_t* set)

set:输出型参数,返回pending位图。

4、被阻塞的信号产生时将保持在未决状态,知道进程解除阻塞才能递达。

5、只要信号被阻塞就不会递达,忽略是递达的一种信号处理。

五、信号处理

其实signal函数中的handler参数不仅仅可以传自定义方法,SIG_IGN是忽略处理,SIG_DFL是默认处理。

上图可抽象成下图

六、重新理解虚拟地址空间

内核级页表只要维护一份,所以进程如何切换都能找到OS

七、键盘输入数据过程

所以信号就是模拟硬件中断来发送信号,同样是异步处理。

八、操作系统如何运行?

本质是死循环 + 时钟中断不停调度系统任务。

九、如何执行系统调用?

系统中维护了一批系统调用的函数指针数组,找到指定的系统调用号就能找到函数指针数组下标,找到函数,以fork()函数为例

pid_t fork()

{

        mov 2 eax; // 把系统调用号放入eax寄存器

        int 0x80 // 外部直接让CPU执行系统调用(陷阱或缺陷)

        ...

}

操作系统不相信任何人,用户无法直接跳到内核空间执行系统调用,只有CPU执行代码时从用户态(3)转到内核态(0)才能跳转。

十、信号的捕捉

与signal()函数类似的,int sigaction(int signum, const struct sigaction* act, struct sigaction* oact);

可以用于信号捕捉。

在sigaction结构体中,sa_handler是捕捉后的处理方法,sa_mask是在处理signum信号时,要屏蔽的信号集。

act:输入型参数,捕捉信号的处理方式。

oldact:输出型参数,旧的信号处理方式。

如果正在对信号进行处理,默认该信号被屏蔽,只有处理完之后自动解除屏蔽。

十一、补充知识

1、可重入函数

描述的是一个函数的特点。

一个执行流调用函数时,另一个执行流也调用该函数,称这个函数被重复进入。如果导致了问题,该函数就是不允许被重复进入,就是不可重入函数,这种函数占据绝大部分。

所以可以重复进入的函数就是可重入函数。

不可重入的条件:

调用了内存管理相关函数

调用了标准 I/O 库函数,因为其中很多实现都以不可重入的方式使用数据结构

2、volatile关键字

避免编译器优化,保证内存可见性。

在定义一个全局变量后,作为计算器的CPU会从内存中读入数据放进寄存器,而每一次从内存中读入数据的操作会因为编译器的优化而取消,这是如果内存中修改了全局变量,CPU寄存器中就不会改变,导致代码执行结果不在预期,此时加上volatile就可以避免问题。

3、SIGCHLD信号

子进程退出会给父进程发送SIGCHLD信号。

重谈进程等待

1、如果不关心子进程的退出情况,父进程可以 signal(SIGCHLD, SIG_IGN)收到子进程的退出信号后忽略信号,避免僵尸进程。

2、如果关心那就要等待,既然已经知道子进程退出会有信号,我们就可以不用在父进程的主程序中等待,可以在父进程收到信号后在处理函数中死循环等待子进程即可。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • LinuxC高级day05(函数指针、条件编译)
  • Python酷库之旅-第三方库Pandas(078)
  • Python知识点:如何使用Arcade进行简易游戏开发
  • 手机电量消耗分析工具 Battery Historian 指南
  • matlab求解方程
  • redis面试(十四)公平锁可重入
  • 【Linux入门】root密码忘记了怎么办?
  • 乳制品企业怎么防止信息泄露?使用加密软件保障数据安全
  • laravel 11 使用jw-auth进行API 登录
  • vs2022 启动之后崩溃解决方案
  • 学习嵌入式入门(十)高级定时器简介及实验(下)
  • 关于MariaDB
  • 测试需求分析(四)
  • winform中设置DateTimePicker参数为空
  • 【C++】什么是模板?
  • electron原来这么简单----打包你的react、VUE桌面应用程序
  • Gradle 5.0 正式版发布
  • javascript面向对象之创建对象
  • miniui datagrid 的客户端分页解决方案 - CS结合
  • PAT A1120
  • React Transition Group -- Transition 组件
  • REST架构的思考
  • SQLServer之创建显式事务
  • VirtualBox 安装过程中出现 Running VMs found 错误的解决过程
  • Vue全家桶实现一个Web App
  • 搭建gitbook 和 访问权限认证
  • 记一次删除Git记录中的大文件的过程
  • 看域名解析域名安全对SEO的影响
  • 扑朔迷离的属性和特性【彻底弄清】
  • 前端面试总结(at, md)
  • 前端之Sass/Scss实战笔记
  • 一天一个设计模式之JS实现——适配器模式
  • 你对linux中grep命令知道多少?
  • 【运维趟坑回忆录】vpc迁移 - 吃螃蟹之路
  • ​埃文科技受邀出席2024 “数据要素×”生态大会​
  • ​软考-高级-系统架构设计师教程(清华第2版)【第20章 系统架构设计师论文写作要点(P717~728)-思维导图】​
  • # linux 中使用 visudo 命令,怎么保存退出?
  • #C++ 智能指针 std::unique_ptr 、std::shared_ptr 和 std::weak_ptr
  • #传输# #传输数据判断#
  • #在 README.md 中生成项目目录结构
  • (145)光线追踪距离场柔和阴影
  • (C++)栈的链式存储结构(出栈、入栈、判空、遍历、销毁)(数据结构与算法)
  • (CPU/GPU)粒子继承贴图颜色发射
  • (Matalb回归预测)PSO-BP粒子群算法优化BP神经网络的多维回归预测
  • (保姆级教程)Mysql中索引、触发器、存储过程、存储函数的概念、作用,以及如何使用索引、存储过程,代码操作演示
  • (大众金融)SQL server面试题(1)-总销售量最少的3个型号的车及其总销售量
  • (分享)一个图片添加水印的小demo的页面,可自定义样式
  • (游戏设计草稿) 《外卖员模拟器》 (3D 科幻 角色扮演 开放世界 AI VR)
  • (转)es进行聚合操作时提示Fielddata is disabled on text fields by default
  • **《Linux/Unix系统编程手册》读书笔记24章**
  • .net core使用EPPlus设置Excel的页眉和页脚
  • .net 调用海康SDK以及常见的坑解释
  • .NET/C# 的字符串暂存池
  • .NET与java的MVC模式(2):struts2核心工作流程与原理
  • @ 代码随想录算法训练营第8周(C语言)|Day53(动态规划)