信号的机制——信号的发送与处理

对于硬件触发的,无论是中断,还是信号,肯定是先到内核的,然后内核对于中断和信号处理方式不同。一个是完全在内核里面处理完毕,一个是将信号放在对应的进程 task_struct 里信号相关的数据结构里面,然后等待进程在用户态去处理。当然有些严重的信号,内核会把进程干掉。但是,这也能看出来,中断和信号的严重程度不一样,信号影响的往往是某一个进程,处理慢了,甚至错了,也不过这个进程被干掉,而中断影响的是整个系统。一旦中断处理中有了 bug,可能整个 Linux 都挂了。

有时候,内核在某些情况下,也会给进程发送信号。例如,向读端已关闭的管道写数据时产生 SIGPIPE 信号,当子进程退出时,我们要给父进程发送 SIG_CHLD 信号等。

最直接的发送信号的方法就是,通过命令 kill 来发送信号了。例如,我们都知道的 kill -9 pid 可以发送信号给一个进程,杀死它。

另外,我们还可以通过 kill 或者 sigqueue 系统调用,发送信号给某个进程,也可以通过 tkill 或者 tgkill 发送信号给某个线程。虽然方式多种多样,但是最终都是调用了 do_send_sig_info 函数,将信号放在相应的 task_struct 的信号数据结构中。

  • kill->kill_something_info->kill_pid_info->group_send_sig_info->do_send_sig_info
  • tkill->do_tkill->do_send_specific->do_send_sig_info
  • tgkill->do_tkill->do_send_specific->do_send_sig_info
  • rt_sigqueueinfo->do_rt_sigqueueinfo->kill_proc_info->kill_pid_info->group_send_sig_info->do_send_sig_info

当发现一个进程应该被调度的时候,我们并不直接把它赶下来,而是设置一个标识位 TIF_NEED_RESCHED,表示等待调度,然后等待系统调用结束或者中断处理结束,从内核态返回用户态的时候,调用 schedule 函数进行调度。信号也是类似的,当信号来的时候,我们并不直接处理这个信号,而是设置一个标识位 TIF_SIGPENDING,来表示已经有信号等待处理。同样等待系统调用结束,或者中断处理结束,从内核态返回用户态的时候,再进行信号的处理。

signal_wake_up_state 的第二件事情,就是试图唤醒这个进程或者线程。wake_up_state 会调用 try_to_wake_up 方法。这个函数我们讲进程的时候讲过,就是将这个进程或者线程设置为 TASK_RUNNING,然后放在运行队列中,这个时候,当随着时钟不断的滴答,迟早会被调用。如果 wake_up_state 返回 0,说明进程或者线程已经是 TASK_RUNNING 状态了,如果它在另外一个 CPU 上运行,则调用 kick_process 发送一个处理器间中断,强制那个进程或者线程重新调度,重新调度完毕后,会返回用户态运行。这是一个时机会检查 TIF_SIGPENDING 标识位。

一个信号中断系统调用的典型逻辑。

首先,我们把当前进程或者线程的状态设置为 TASK_INTERRUPTIBLE,这样才能使这个系统调用可以被中断。

其次,可以被中断的系统调用往往是比较慢的调用,并且会因为数据不就绪而通过 schedule 让出 CPU 进入等待状态。在发送信号的时候,我们除了设置这个进程和线程的 _TIF_SIGPENDING 标识位之外,还试图唤醒这个进程或者线程,也就是将它从等待状态中设置为 TASK_RUNNING。

当这个进程或者线程再次运行的时候,我们根据进程调度第一定律,从 schedule 函数中返回,然后再次进入 while 循环。由于这个进程或者线程是由信号唤醒的,而不是因为数据来了而唤醒的,因而是读不到数据的,但是在 signal_pending 函数中,我们检测到了 _TIF_SIGPENDING 标识位,这说明系统调用没有真的做完,于是返回一个错误 ERESTARTSYS,然后带着这个错误从系统调用返回。

然后,我们到了 exit_to_usermode_loop->do_signal->handle_signal。在这里面,当发现出现错误 ERESTARTSYS 的时候,我们就知道这是从一个没有调用完的系统调用返回的,设置系统调用错误码 EINTR。

信号的发送与处理是一个复杂的过程,这里来总结一下。

  1. 假设我们有一个进程 A,main 函数里面调用系统调用进入内核。
  2. 按照系统调用的原理,会将用户态栈的信息保存在 pt_regs 里面,也即记住原来用户态是运行到了 line A 的地方。
  3. 在内核中执行系统调用读取数据。
  4. 当发现没有什么数据可读取的时候,只好进入睡眠状态,并且调用 schedule 让出 CPU,这是进程调度第一定律。
  5. 将进程状态设置为 TASK_INTERRUPTIBLE,可中断的睡眠状态,也即如果有信号来的话,是可以唤醒它的。
  6. 其他的进程或者 shell 发送一个信号,有四个函数可以调用 kill、tkill、tgkill、rt_sigqueueinfo。
  7. 四个发送信号的函数,在内核中最终都是调用 do_send_sig_info。
  8. do_send_sig_info 调用 send_signal 给进程 A 发送一个信号,其实就是找到进程 A 的 task_struct,或者加入信号集合,为不可靠信号,或者加入信号链表,为可靠信号。
  9. do_send_sig_info 调用 signal_wake_up 唤醒进程 A。
  10. 进程 A 重新进入运行状态 TASK_RUNNING,根据进程调度第一定律,一定会接着 schedule 运行。
  11. 进程 A 被唤醒后,检查是否有信号到来,如果没有,重新循环到一开始,尝试再次读取数据,如果还是没有数据,再次进入 TASK_INTERRUPTIBLE,即可中断的睡眠状态。
  12. 当发现有信号到来的时候,就返回当前正在执行的系统调用,并返回一个错误表示系统调用被中断了。
  13. 系统调用返回的时候,会调用 exit_to_usermode_loop。这是一个处理信号的时机。
  14. 调用 do_signal 开始处理信号。
  15. 根据信号,得到信号处理函数 sa_handler,然后修改 pt_regs 中的用户态栈的信息,让 pt_regs 指向 sa_handler。同时修改用户态的栈,插入一个栈帧 sa_restorer,里面保存了原来的指向 line A 的 pt_regs,并且设置让 sa_handler 运行完毕后,跳到 sa_restorer 运行。
  16. 返回用户态,由于 pt_regs 已经设置为 sa_handler,则返回用户态执行 sa_handler。
  17. sa_handler 执行完毕后,信号处理函数就执行完了,接着根据第 15 步对于用户态栈帧的修改,会跳到sa_restorer 运行。
  18. sa_restorer 会调用系统调用 rt_sigreturn 再次进入内核。
  19. 在内核中,rt_sigreturn 恢复原来的 pt_regs,重新指向 line A。
  20. 从 rt_sigreturn 返回用户态,还是调用 exit_to_usermode_loop。
  21. 这次因为 pt_regs 已经指向 line A 了,于是就到了进程 A 中,接着系统调用之后运行,当然这个系统调用返回的是它被中断了,没有执行完的错误。

此文章为11月Day18学习笔记,内容来源于极客时间《趣谈Linux操作系统》,推荐该课程。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

紫色调城市和奔跑人物剪影背景工会工作总结汇报PPT模板

这是一套紫色调城市和奔跑人物剪影背景工会工作总结汇报PPT模板,共33页; PPT模板封面,使用了蓝天白云、城市剪影、奔跑人物剪影背景图片。中间填写工会工作总结汇报PPT标题。界面色彩丰富充满活力。 PowerPoint模板内容页,由31张…

gittee启动器

前言 很多小伙伴反馈不是使用gitee,不会寻找好的项目,在拿到一个项目不知道从哪里入手。 鼠鼠我呀就是宠粉,中嘞,老乡。整!!! git的基本指令 在使用gitee的时候呢,我们只需要记住…

C++加持让python程序插上翅膀——利用pybind11进行c++和python联合编程示例

目录 0、前言1、安装 pybind11库c侧python侧 2、C引入bybind11vs增加相关依赖及设置cpp中添加头文件及导出模块cpp中添加numpy相关数据结构的接收和返回编译生成dll后改成导出模块同名文件的.pyd 3、python调用c4、C引入bybind11 0、前言 在当今的计算机视觉和机器学习领域&am…

【入门篇】1.4 redis 客户端 之 Lettuce 详解

文章目录 1. 简介1. 什么是Lettuce2. Lettuce与其他Redis客户端的比较3. Lettuce的特性和优势 2. 安装和配置3. 连接池配置1. 什么是连接池2. Lettuce的连接池使用与配置3. 连接池配置项 4. 基本操作1. 如何创建Lettuce连接2. Lettuce的基本操作如增删改查3. Lettuce的事务操作…

vue --version无法显示,只弹出vs窗口

参考连接: nodejs环境配置(解压包)安装教程_nodejs解压版安装及环境配置_tubond的博客-CSDN博客 原因:环境没搞好,没有设置全局文件夹,node默认放在C盘了,C盘有权限。因为npm -i vue/cli创建…

Vite - 配置 - 文件路径别名的配置

为什么要配置别名 别名的配置,主要作用是为了缩短代码中的导入路径。例如有如下的项目目录: project-name| -- src| -- a| --b| --c| --d| --e| -- abc.png| -- index.html| -- main.js如果想在 main.js 文件中使用 abc.png ,则使用的路径是 &#xff1…

【智能家居项目】FreeRTOS版本——多任务系统中使用DHT11 | 获取SNTP服务器时间 | 重新设计功能框架

🐱作者:一只大喵咪1201 🐱专栏:《智能家居项目》 🔥格言:你只管努力,剩下的交给时间! 目录 🍓多任务系统中使用DHT11🍅关闭调度器🍅使用中断 &am…

人类智能的精髓超出了统计概率

处理不确定性好坏的程度是衡量各种智能系统高低的一个重要指标。在处理不确定性时,智能系统需要具备推理、学习和决策的能力,通常使用概率和统计等方法来建模和处理不确定性,以便更好地应对现实世界中的复杂问题。统计概率是基于大量观察和数…

【Spring总结】基于配置的方式来写Spring

本篇文章是对这两天所学的内容做一个总结,涵盖我这两天写的所有笔记: 【Spring】 Spring中的IoC(控制反转)【Spring】Spring中的DI(依赖注入)Dependence Import【Spring】bean的基础配置【Spring】bean的实…

一键云端,AList 整合多网盘,轻松管理文件多元共享!

hello,我是小索奇,本篇教大家如何使用AList实现网盘挂载 可能还是有小伙伴不懂,所以简单介绍一下哈 AList 是一款强大的文件管理工具,为用户提供了将多种云存储服务和文件共享协议集成在一个平台上的便利性。它的独特之处在于&am…

彩色年终工作总结汇报PPT模板下载

这是一套彩色年终工作总结汇报PPT模板,共27页; PPT模板封面,使用了红黄蓝色块、网格背景。中间填写年终工作总结汇报PPT标题。界面为简约商务风格。 PowerPoint模板内容页,由25张彩色动态幻灯片图表,搭配PPT文字排版…

Python 双门双向门禁控制板实时监控源码

本示例使用设备:实时网络双门双向门禁控制板可二次编程控制网络继电器远程开关-淘宝网 (taobao.com) #python通过缩进来表示代码块,不可以随意更改每行前面的空白,否则程序会运行错误!!!如果缩进不一致&a…

Python---函数练习:编写一个打招呼程序

函数的定义-------相关链接:Python---函数的作用,定义,使用步骤(调用步骤)-CSDN博客基本语法: def 函数名称([参数1, 参数2, ...]):函数体...[return 返回值] 函数的调用 Python中,函数和变量一…

2023年【危险化学品经营单位安全管理人员】考试题及危险化学品经营单位安全管理人员模拟试题

题库来源:安全生产模拟考试一点通公众号小程序 危险化学品经营单位安全管理人员考试题是安全生产模拟考试一点通总题库中生成的一套危险化学品经营单位安全管理人员模拟试题,安全生产模拟考试一点通上危险化学品经营单位安全管理人员作业手机同步练习。…

MSYS2介绍及工具安装

0 Preface/Foreword 1 MSYS2 官网:MSYS2

服务容错之限流之 Tomcat 限流 Tomcat 线程池的拒绝策略

在文章开头,先和大家抛出两个问题: 每次提到服务限流为什么都不考虑基于 Tomcat 来做呢?大家有遇到过 Tomcat 线程池触发了拒绝策略吗? JUC 线程池 在谈 Tomcat 的线程池前,先看一下 JUC 中线程池的执行流程&#x…

[acwing周赛复盘] 第 94 场周赛20230311

[acwing周赛复盘] 第 94 场周赛20231118 总结5295. 三元组1. 题目描述2. 思路分析3. 代码实现 5296. 边的定向1. 题目描述2. 思路分析3. 代码实现 六、参考链接 总结 好久没做acw了,挺难的。T1 模拟T2 前缀和以及优化。T3 贪心 5295. 三元组 链接: 5295. 三元组…

操作系统(存储管理进程管理设备管理)

文章目录 存储管理页式存储管理概念优点缺点页面置换算法快表(很快速的页表) 段式存储管理概念优点缺点 段页式存储管理概念优点缺点 进程管理概述作用特征功能分类计算机启动基本流程 进程管理进程的组成进程的基础状态前趋图进程资源图同步和互斥信号量…

os.path.join函数用法

os.path.join()是Python中用于拼接文件路径的函数,它可以将多个字符串拼接成一个路径,并且会根据操作系统的规则自动使用合适的路径分隔符。 注:Linux用的是/分隔符,而Windows才用的是\。 该函数属于os.path模块,因此在…

解决Redis分布式锁宕机出现不可靠问题-zookeeper分布式锁

核心思想:当客户端要获取锁,则创建节点,使用完锁,则删除该节点。 客户端获取锁时,在 lock 节点下创建临时顺序节点。然后获取 lock下面的所有子节点,客户端获取到所有的子节点之后,如果发现自己…