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

linux进程信号II

目录

信号保存

概念补充

sigset_t类型

信号集操作函数

sigprocmask(修改block)

sigpending(获取pending)

小结

信号处理

sigaction

内核态和用户态的切换

CPL

硬件中断

概念

意义

时钟中断

软中断(又叫陷阱)

软中断辅助实现系统调用

从内核角度看

小结

缺页中断

可重入函数(问题)

不可重入函数

volatile(易变的,不稳定的)

SIGCHLD(17号信号)

问题

小知识


信号保存

概念补充

1.信号递达:实际执行信号的处理动作


2.信号未决:信号在产生到递达之间的状态


3.进程可以阻塞某个信号(屏蔽某个信号)


4.进程的PCB中有两位图用来接受信号

pending    比特位位置:信号编号          比特位内容:是否收到

block        比特位位置:信号编号          比特位内容:是否阻塞(屏蔽)

用一个位图来处理信号

handler位图表 位置代表信号编号,内容是一个指向信号处理方法的指针


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


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

sigset_t类型

称为信号集 是一个位图结构类型 ,pending和block都是此类型

其中block(阻塞信号集)又叫做信号屏蔽字

信号集操作函数

1.sigemptyset初始化set所指向的信号集,使其中所有信号的对应bit位清零,表示该信号集不包含任何有效信号。

2.sigfillset初始化set所指向的信号集,使其中所有信号的对应bit位置1,表示该信号集的有效信号,包括系统支持的所有信号。

3.sigaddset() 设置一个sigset_t类型变量的对应位置为1

4.sigisnumber()判断一个信号是否在集合里 条件为真返回值大于0,为假返回0

5.四个函数都是成功返回0,出错返回-1。

sigprocmask(修改block)

how表示如何更改

set:

oldset:oldset是输出型参数,是修改前的block

sigpending(获取pending)

set:输出型参数,获取当前的pending

成功返回0,出错返回-1

小结

1.是否收到和是否阻塞无关系,如果一个信号被block,即便收到,也不能被递达

2.9号信号不可被捕捉,不可被屏蔽(管理员信号)

3.信号递达前把pending置0,然后去递达(执行处理函数)

信号处理

注:当某个信号处理函数被调用时,内核将自动将信号屏蔽,等处理完解除屏蔽,防止多个信号到来造成死递归.

sigaction

使用时要构造一个struct sigaction 传到act位置

oldact表示上一个act

sa_mask表示如果用户想除了正在处理的信号之外的额外屏蔽其他信号,可以将其加进来

内核态和用户态的切换

代码执行时,只有内核态和用户态两种执行模式

内核态:操作系统运行时的状态,主要用来执行内核代码

用户态:用CPU执行用户自己的代码的状态

大体流程:

所以OS处理自定义捕捉的信号比较费劲,处理默认的信号时,不需返回用户态,执行自定义的时,要返回用户态去(因为自定义函数在用户空间的某块栈帧中.要由用户去执行),执行完自定义信号处理函数后再执行sigreturn返回内核态,之后再到用户态上次被中断的地方继续执行

CPL

CPU中有寄存器cs,寄存器内存着执行级别CPL(Current Privilege Level)


CPL两种级别 : 0表示内核 3表示用户 ,在内核和用户页表中也有CPL,寄存器cs和页表中CPL相同时,页表才能正确映射,才能正常执行,防止用户使用野指针修改内核区

硬件中断

概念

CPU会放弃正在执行的代码,去执行这个硬件的代码.


1.每种外设都可以向CPU发送硬件中断

2.会有一个中断向量表(OS提供的)存各种中断处理方法

意义

硬件中断的意义是让OS不需要周期性检测外设,只需要等待中断到来再查表执行,提高效率.

时钟中断

当代计算机,CPU中会集成时钟源,这个硬件可以定期向OS发送中断信号,来推动OS的执行,所以OS这时可以看做一个中断向量表,这样OS就在硬件的推动下"自动"调度了,OS就是一个死循环的代码

每个一段时间,触发一次时钟中断,在这个时间段内,执行进程代码

软中断(又叫陷阱)

CPU通过识别汇编指令来执行中断就是软中断

为了让操作系统支持系统调用,CPU设计了对应的汇编指令(int 或 syscall),可以让CPU内部触发中断

int 通过触发软中断进入内核态,执行操作系统的代码

syscall  专为64位系统设计的进入内核执行系统调用的指令

软中断辅助实现系统调用

1.系统调用:  先int/syscall 陷入内核,本质就是触发软中断,CPU会根据系统调用号自动查表,执行对应方法

2.用户层通过寄存器将系统调用号给操作系统

3.操作系统通过寄存器或者用户传入的缓冲区地址将返回值给用户

4.系统调用号的本质:数组下标

从内核角度看

1.所有的系统调用方法都在全局的系统调用表里,使用时,只需要对应的数组下标

2.系统调用号,就是这个数组下标

3.对于每个进程来说,都有自己的用户级页表,而所有进程共用一个内核级页表

小结

1.真正的系统调用不是C语言风格的,我们使用的是C语言封装好的接口.

2.各种编程语言想在linux上运行,就必须通过C语言系统调用,这就是 C生万物.

缺页中断

直接申请一块空间时,不会在物理内存中直接开,只是在虚拟内存中开,当访问时,触发缺页中断,执行对应中断方法,再真正在物理内存开辟空间.

可重入函数(问题)

进程运行中,任何时间都有可能进行信号的递达,因为时间片可能到了

假如有一个insert函数,用来给全局链表进行插入操作,insert函数在执行中进程时间片到了恰好执行时钟中断,就进入了内核态执行sighandler,如果内核态又调用insert函数,insert函数就被重入了,等返回用户态继续执行,这时插入会发生错误,图中node2结点丢失,造成内存泄漏问题,所以这种函数应为不可重入函数

不可重入函数

1.使用new或malloc,因为使用了全局链表来管理

2.调用标准I/O库函数,标准I/O库函数的很多实现都以不可重入的方式使用全局数据结构

3.使用全局变量的函数

volatile(易变的,不稳定的)

C语言C90标准32关键字之一,叫做易变关键字,用来处理编译器的优化导致的问题

这个代码如果加上编译器优化,当给进程发2号信号时,进程就不会结束了,为什么呢

1.优化前:代码执行,CPU对变量进行检测,先把变量放到寄存器中,然后对变量进行运算,最后将变量写回内存,这个过程不断执行.

2.优化后:直接将变量的值写进寄存器中,作为寄存器变量,只做检测,不进行变量的读取和写入,导致全局变量改变,CPU中的寄存器变量不变,进程不会结束

编译器的优化,屏蔽了内存数据.

 

所以volatile用来禁止编译器对变量(还有指针等)的优化,保持内存可见性.

SIGCHLD(17号信号)

子进程退出时,会给父进程发送17号信号,只不过默认递达方式是忽略

所以,可以直接在信号处理函数中进行子进程的waitpid,父进程就不需轮询,等子进程结束发信号就自动回收了

问题

1.如果多个子进程,同时发信号时,由于一个信号在执行时,其他的会被block,最终可能只回收1个子进程

while循环解决

2..如果多个子进程,一部分退了,一部分没退,循环只能一起回收一起退的,所以还要加判断

WNOHANG :waitpid阻塞时,会直接返回0,不阻塞.

小知识

1.OS在调度进程时,一直在进行内核到用户,用户到内核的转换,因为还有时钟中断,进程时间片,调度


2.register     建议关键字,建议将声明的变量放到寄存器中,只是建议


3.linux要想不产生僵尸进程还有一种方法,父进程调用sigaction将SIGCHLD的处理动作置为SIG_IGN,这样fork出来的子进程在终止时会自动清理,不产生僵尸进程,也不通知父进程.系统默认忽略动作和用户sigaction函数定义的忽略动作通常是没有区别的,但这是一个特例.

http://www.lryc.cn/news/586867.html

相关文章:

  • Node.js特训专栏-实战进阶:16. RBAC权限模型设计
  • 基于YOLOv7的改进模型:集成Swin Transformer和ASFF模块
  • 26-计组-数据通路
  • 【软件开发】使用 Spring WebFlux 进行请求校验
  • iOS ish app 打印时间
  • HJ8 合并表记录 10:35
  • Vue中的render()函数
  • 【LeetCode数据结构】单链表的应用——反转链表问题、链表的中间节点问题详解
  • 为什么要有延时回调?
  • 【实证分析】上市公司绿色战略数据集(2000-2023年)
  • 如何设计一个合理的 Java Spring Boot 项目结构
  • C++ 强制类型转换
  • 【读书笔记】《C++ Software Design》第六章深入剖析 Adapter、Observer 和 CRTP 模式
  • 开机自动启动同花顺,并设置进程优先级为高
  • Linux驱动开发1:设备驱动模块加载与卸载
  • 【Linux学习笔记】认识信号和信号的产生
  • JAVA JVM虚拟线程
  • HTML 初体验
  • 软件文档体系深度解析:工程视角下的文档架构与治理
  • OneCode3.0 VFS分布式文件管理API速查手册
  • jenkins使用Jenkinsfile部署springboot+docker项目
  • 代码随想录|图论|15并查集理论基础
  • Docker一键安装中间件(RocketMq、Nginx、MySql、Minio、Jenkins、Redis)脚步
  • SDN软件定义网络架构深度解析:分层模型与核心机制
  • Redis缓存设计与性能优化指南
  • 解码冯・诺依曼:操作系统是如何为进程 “铺路” 的?
  • [Nagios Core] CGI接口 | 状态数据管理.dat | 性能优化
  • 基于Redis Streams的实时消息处理实战经验分享
  • Appium源码深度解析:从驱动到架构
  • 使用macvlan实现容器的跨主机通信