Linux进程信号

全文目录

  • 概念
    • 什么是Linux信号?
    • 信号行为(core dump)
    • 如何理解信号被进程保存:
    • 信号发送的本质:
  • 产生信号
    • 1. 终端按键(组合键)变成信号:
    • 2. 通过系统调用接口向进程发送信号
    • 3. 软件条件产生信号
    • 4. 硬件异常产生信号
    • 总结
  • 信号阻塞
    • 概念
    • 在内核中的表示
    • sigset_t (信号集)
    • 信号集操作函数
  • 信号捕捉
    • 信号捕捉流程
    • signal函数
    • sigaction
  • 可重入函数
  • volatile
  • SIGCHLD信号

概念

信号与信号量是不同的概念。

什么是Linux信号?

本质上是一种通知机制,用户或者OS通过发送信号,告诉进程需要做什么。
例如:ctrl + c 本质就是向进程发送2号信号,终止进程。

  1. 进程要处理信号,必须具备信号“识别”的能力(看到 + 处理动作)
  2. 信号的处理不是立即的
  3. 信号会临时记录对应的信号,方便后续处理
  4. 一般而言,信号的产生相对与进程而言是异步的

通过kill -l 可以察看系统定义的信号列表:

在这里插入图片描述

[1,31]普通信号,[34,64]实时信号

信号行为(core dump)

通过man -7 signal 查看信号的处理动作

在这里插入图片描述

在这里插入图片描述

  • T e r m ( t e r m i n a l ) Term(terminal) Term(terminal) 表示终止进程
  • C o r e ( C o r e D u m p ) Core(Core Dump) Core(CoreDump) 表示核心转储,默认关闭。
    在进程等待中也出现了这个名词:
    在这里插入图片描述

首先解释什么是 C o r e D u m p Core Dump CoreDump。当一个进程要异常终止时,可以选择把进程的用户空间内存数据全部 保存到磁盘上,文件名通常是 c o r e core core,这叫做 C o r e D u m p Core Dump CoreDump。进程异常终止通常是因为有 B u g Bug Bug,比如非法内存访问导致段错误,事后可以用调试器检查 c o r e core core 文件以查清错误原因,这叫做 P o s t − m o r t e m D e b u g Post-mortem Debug PostmortemDebug(事后调试)。一个进程允许产生多大的 c o r e core core文件取决于进程的 R e s o u r c e L i m i t Resource Limit ResourceLimit(这个信息保存 在 P C B PCB PCB 中)。默认是不允许产生 c o r e core core 文件的,因为 c o r e core core 文件中可能包含用户密码等敏感信息,不安全。在开发调试阶段可以用 u l i m i t ulimit ulimit 命令改变这个限制,允许产生 c o r e core core 文件。 首先用 u l i m i t ulimit ulimit 命令改变 S h e l l Shell Shell 进程的 R e s o u r c e L i m i t Resource Limit ResourceLimit ,允许 c o r e core core 文件最大为 1024 K 1024K 1024K : $ ulimit -c 1024

在gdb中可以通过core-file core文件 命令来加载core文件,直接将定位到出错位置
在这里插入图片描述

如何理解信号被进程保存:

表示信号有两点:

  1. 什么信号
  2. 是否产生

进程必须通过数据结构来保存信号(位图),也就是在进程PCB内部有信号位图字段

信号发送的本质:

OS 向目标进程发送信号就是修改其信号位图

产生信号

1. 终端按键(组合键)变成信号:

ctrl + c   	# 终止进程

如何理解终端按键(组合键)变成信号:

键盘工作的方式是通过:中断方式进行的,能够识别每个按键,同样能识别组合键。
OS解释组合键 ——> 查看进程列表 ——> 前台运行的进程 ——> OS 写入对应信号到进程内部的位图结构中

2. 通过系统调用接口向进程发送信号

在这里插入图片描述

kill命令就是通过调用kill函数实现的

在这里插入图片描述

如何理解系统调用产生信号:

用户调用系统调用 ——> 执行OS的系统调用代码 ——> OS 提取参数 ——> OS向目标进程写入信号 ——> 修改对应进程的信号标志位 ——> 进程处理信号 ——> 执行对应的处理动作

3. 软件条件产生信号

  • 管道读端关闭,写端一直写,写的进程会自动退出,就是因为OS向该进程发送了 14) SIGPIPE 信号。

  • alarm函数

在这里插入图片描述

如何理解软件条件产生信号:

OS先识别到某种软件条件触发或不满足 ——》 OS 构建信号,发送给指定信号

4. 硬件异常产生信号

  1. 浮点数溢出错误

当程序中发生除0时,就会发生 Floating point exception(浮点数溢出) 错误,就是产生了8) SIGFPE信号。默认情况下会直接终止进程,如果通过signal自定义行为就会一直执行自定义的行为,为什么呢?

如何理解除0

进行计算的时CPU这个硬件,CPU内部是有寄存器的,对于计算状态有一个单独的状态寄存器(位图),发生了浮点数溢出错误溢出标志位就会被设为1,后面每次都会检测状态寄存器都会立即检测到溢出状态并向对应进程发送 8 号信号,但是状态寄存器里面溢出标志位不会被清空,所以该进程一直都是溢出状态,一直向进程发送 8 号信号

  1. 野指针或越界错误

访问野指针或者越界会触发 11) SIGSEGV 信号,发生 segment fault(段错误)

如何理解段错误:

我们拿到的地址都是虚拟地址,访问目标地址时需要通过页表 + MMU(Memory Manage Unit,硬件)当转换成物理地址。如果时非法地址,MMU转换时会报错,也就是产生 11 号信号

总结

所有的信号都是由OS识别并发送的。

信号阻塞

概念

  • 实际执行信号的处理动作称为信号递达(Delivery)
  • 信号从产生到递达之间的状态,称为信号未决(Pending)
  • 进程可以选择阻塞 (Block )某个信号。
  • 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作.
  • 注意:阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。

在内核中的表示

在这里插入图片描述

  • SIG_DFLSIG_IGN 是宏,转换成指针函数的整数 0 和 1,进行信号递达前需要先将信号处理函数转换成整数,判断是忽略函数进行默认动作。
  • 9) SIGKILL 不会被阻塞
  • 每个信号都有两个标志位分别表示阻塞(block)和未决(pending),还有一个函数指针表示处理动作。信号产生时,内核在进程控制块中设置该信号的未决标志,直到信号递达才清除该标志。在上图的例子中,SIGHUP信号未阻塞也未产生过,当它递达时执行默认处理动作。
  • 信号产生没有递达会一直处于未决状态,如果信号阻塞,信号将一直处于未决状态。即便是忽略信号也是如此
  • 常规信号在递达之前产生多次只计一次,而实时信号在递达之前产生多次可以依次放在一个队列里。

例子:

  • SIGINT信号产生过,但正在被阻塞,所以暂时不能递达。虽然它的处理动作是忽略,但在没有解除阻塞之前不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除阻塞。
  • SIGQUIT信号未产生过,一旦产生SIGQUIT信号将被阻塞,它的处理动作是用户自定义函数sighandler。如果在进程解除对某信号的阻塞之前这种信号产生过多次,将如何处理?POSIX.1允许系统递送该信号一次或多次。Linux是这样实现的:常规信号在递达之前产生多次只计一次,而实时信号在递达之前产生多次可以依次放在一个队列里。

sigset_t (信号集)

每个信号只有一个bit的未决标志,非0即1,不记录该信号产生了多少次,阻塞标志也是这样表示的。因此,未决和阻塞标志可以用相同的数据类型sigset_t来存储,sigset_t称为信号集,这个类型可以表示每个信号的“有效”或“无效”状态,在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有效”和“无效”的含义是该信号是否处于未决状态。下一节将详细介绍信号集的各种操作。 阻塞信号集也叫做当前进程的信号屏蔽字(Signal Mask),这里的“屏蔽”应该理解为阻塞而不是忽略

信号集操作函数

用户只能通过特定的函数才能操作信号集:

#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);这四个函数都是成功返回0,出错返回-1。sigismember是一个布尔函数,
用于判断一个信号集的有效信号中是否包含某种信号,若包含则返回1,不包
含则返回0,出错返回-1
  • 函数sigemptyset初始化set所指向的信号集,使其中所有信号的对应bit清零,表示该信号集不包含 任何有效信号。
  • 函数sigfillset初始化set所指向的信号集,使其中所有信号的对应bit置位,表示该信号集的有效信号包括系统支持的所有信号。
  • 注意,在使用sigset_t类型的变量之前,一定要调 用sigemptysetsigfillset做初始化,使信号集处于确定的状态。初始化sigset_t变量之后就可以在调用sigaddsetsigdelset在该信号集中添加或删除某种有效信号

在这里插入图片描述

[外链图片转存中…(img-6d1bac13463fafe114937ff59004.png)

信号捕捉

信号有三种处理方法:

  1. 默认行为(OS定义的行为)
  2. 忽略(不做任何处理)
  3. 自定义行为(用户自定义的行为)

如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。

信号捕捉流程

在这里插入图片描述

简化一下就是:

在这里插入图片描述

如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。由于信号处理函数的代码是在用户空间的,处理过程比较复杂,举例如下: 用户程序注册了SIGQUIT信号的处理函数sighandler。 当前正在执行main函数,这时发生中断或异常切换到内核态。 在中断处理完毕后要返回用户态的main函数之前检查到有信号SIGQUIT递达。 内核决定返回用户态后不是恢复main函数的上下文继续执行,而是执行sighandler函 数,sighandler和main函数使用不同的堆栈空间,它们之间不存在调用和被调用的关系,是 两个独立的控制流程。 sighandler函数返回后自动执行特殊的系统调用sigreturn再次进入内核态。 如果没有新的信号要递达,这次再返回用户态就是恢复main函数的上下文继续执行了。

进程如何从用户态进入内核态:

CPU有两套寄存器,一套用来计算,一套自用,自用中有一个CR3表示当前CPU的权限。发生异常或者调用系统调用时,会先执行 int 80 将权限转为内核态。想要执行进程地址空间中的 3~4G 系统代码,也是如此。

通过signal函数自定义对应信号的捕捉动作

signal函数

在这里插入图片描述

9号信号不会被捕捉

sigaction

#include <signal.h>int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);struct sigaction {void     (*sa_handler)(int); 	# 信号处理方法void     (*sa_sigaction)(int, siginfo_t *, void *);sigset_t   sa_mask; 	# 信号集int        sa_flags;void     (*sa_restorer)(void);
};参数:sigaction函数可以读取和修改与指定信号相关联的处理动作。调用成功则返回0,出错则返回- 1。signo是指定信号的编号。若act指针非空,则根据act修改该信号的处理动作。若oact指针非 空,则通过oact传出该信号原来的处理动作。act和oact指向sigaction结构体:将sa_handler赋值为常数SIG_IGN传给sigaction表示忽略信号,赋值为常数SIG_DFL表示执行系统默认动作,赋值为一个函数指针表示用自定义函数捕捉信号,或者说向内核注册了一个信号处理函 数,该函数返回值为void,可以带一个int参数,通过参数可以得知当前信号的编号,这样就可以用同一个函数处理多种信号。显然,这也是一个回调函数,不是被main函数调用,而是被系统所调用。

处理信号时的信号屏蔽:

当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么 它会被阻塞到当前处理结束为止。 如果在调用信号处理函数时,除了当前信号被自动屏蔽之外,还希望自动屏蔽另外一些信号,则用sa_mask字段说明这些需要额外屏蔽的信号,当信号处理函数返回时自动恢复原来的信号屏蔽字。

可重入函数

在这里插入图片描述

main函数调用insert函数向一个链表head中插入节点node1,插入操作分为两步,刚做完第一步的 时候,因为硬件中断使进程切换到内核,再次回用户态之前检查到有信号待处理,于是切换 到sighandler函数,sighandler也调用insert函数向同一个链表head中插入节点node2,插入操作的 两步都做完之后从sighandler返回内核态,再次回到用户态就从main函数调用的insert函数中继续 往下执行,先前做第一步之后被打断,现在继续做完第二步。结果是,main函数和sighandler先后 向链表中插入两个节点,而最后只有一个节点真正插入链表中了。

像上例这样,insert函数被不同的控制流程调用,有可能在第一次调用还没返回时就再次进入该函数,这称为重入,insert函数访问一个全局链表,有可能因为重入而造成错乱,像这样的函数称为 不可重入函数,反之,如果一个函数只访问自己的局部变量或参数,则称为可重入(Reentrant) 函数大部分的函数都是不可重入的,可重入函数的成本要比不可重入函数的成本高得多

如果一个函数符合以下条件之一则是不可重入的:

  • 调用了malloc或free,因为malloc也是用全局链表来管理堆的。

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

volatile

有时编译器将会对代码进行优化,例如:一个全局变量在main函数中没有被修改,那么CPU访问该变量时将会是直接通过寄存器访问,也就是内存不可见。当该变量在函数中被修改时,可能会影响到程序的运行结果。

volatile可以解决该问题,保持内存可见性

volatile int flag = 0;

SIGCHLD信号

子进程在终止时会给父进程发SIGCHLD信号,该信号的默认处理动作是忽略,父进程可以自 定义SIGCHLD信号的处理函数,这样父进程只需专心处理自己的工作,不必关心子进程了,子进程 终止时会通知父进程,父进程在信号处理函数中调用wait清理子进程即可。

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

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

模拟实现消息队列项目(系列3) -- 服务器模块(硬盘管理)

目录 前言 1. 创建项目 2. 创建核心类 2.1 Exchange 2.2 MSQueue 2.3 Binding 2.4 Message 3. 数据库设计 3.1 SQLite 配置 3.2 Mapper层代码实现 3.2.1 创建表操作 3.2.2 交换机 队列 绑定的增加和删除 3.3 实现DataBaseManager 3.4 DataBaseManager单元测试 4.…

“他“是怎么拿offer的?全网最全,性能测试面试题+答案(超全整理)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、什么是负载测试…

R语言3_安装SeurateData

环境Ubuntu22/20, R4.1 在命令行中键入&#xff0c; apt-get update apt install libcurl4-openssl-dev libssl-dev libxml2-dev libcairo2-dev libgtk-3-dev # libcairo2-dev :: systemfonts # libgtk :: textshaping进入r语言交互环境&#xff0c;键入&#xff0c; instal…

7.物联网操作系统互斥信号量

1.使用互斥信号量解决信号量导致的优先级反转&#xff0c; 2.使用递归互斥信号量解决互斥信号量导致的死锁。 3.高优先级主函数中多次使用同一信号量的使用&#xff0c;使用递归互斥信号量&#xff0c;但要注意每个信号量的使用要对应一个释放 优先级翻转问题 优先级翻转功能需…

Git仓关联多个远程仓路径

前言 Git仓如果需要将代码push到多个仓&#xff0c;常用的做法是添加多个远程仓路径&#xff0c;然后分别push。这样虽然可以实现目的&#xff0c;但是需要多次执行push指令&#xff0c;很麻烦。 本文介绍关联多个远程仓路径且执行一次push指令的方法&#xff1a;git remote …

conda 环境 numpy 安装报错需要 Microsoft Visual C++ 14.0

到公司装深度学校环境。项目较旧&#xff0c;安装依赖&#xff0c;一堆报错&#xff08;基于 conda 环境&#xff09;&#xff1a; numpy 安装报需要 C 14.0 No module named numpy.distutils._msvccompiler in numpy.distutils; trying from distutilserror: Microsoft Visu…

Maven-生命周期及命令

关于本文 ✍写作原因 之前在学校学习的时候&#xff0c;编写代码使用的项目都是单体架构&#xff0c;导入开源框架依赖时只需要在pom.xml里面添加依赖&#xff0c;点一下reload按钮即可解决大部分需求&#xff1b;但是在公司使用了dubbo微服务架构之后发现只知道使用reload不足…

COSV Schema 1.0正式对外发布,棱镜七彩参与制定工作

近期&#xff0c;CCF版开源漏洞信息描述规范COSV Schema 1.0正式制定并对外发布&#xff0c;棱镜七彩参与制定工作。 图 COSV Schema 1.0制定过程贡献单位及专家名单 作为开源软件治理与软件供应链安全领域的先行者&#xff0c;棱镜七彩一直致力于提升开源效能、防范开源漏洞。…

一起学数据结构(3)——万字解析:链表的概念及单链表的实现

上篇文章介绍了数据结构的一些基本概念&#xff0c;以及顺序表的概念和实现&#xff0c;本文来介绍链表的概念和单链表的实现&#xff0c;在此之前&#xff0c;首先来回顾以下顺序表的特点&#xff1a; 1.顺序表特点回顾&#xff1a; 1. 顺序表是一组地址连续的存储单元依次存…

FL Studio21高级中文版本下载及切换中文语言教程

FL Studio对新人有极高的友好度&#xff0c;成为编曲软件的入门首选&#xff01;FL Studio官方提供多达31款各类插件&#xff0c;令你编曲功力大涨&#xff01;FL Studio是超多顶级音乐人的启蒙首选&#xff01;包括百大DJ冠军Martin Garrix&#xff0c;六获格莱美提名的Deadma…

Java入门2022黑马-200-1

1-5 常用cmd命令 dir可以查看隐藏的文件&#xff0c; exit 退出 6-20 20-30 30-40 37 三元表达式 switch新特性 统计 while continue break 50

ELK企业级日志分析系统

目录 一、ELK 概述 1.ElasticSearch 2.Kiabana 3.Logstash 可以添加的其它组件 1.Filebeat 2.Fluentd 三、为什么要使用 ELK 四、ELK 的工作原理 五、 ELK Elasticsearch 集群部署 更改主机名、配置域名解析、查看Java环境 部署 Elasticsearch 软件 修改elasticsearc…

怎么合并多个视频?简单视频合并方法分享

合并多个视频可以将它们组合成一个更长的视频&#xff0c;这对于需要播放多个短视频的情况非常有用。此外&#xff0c;合并视频还可以使视频编辑过程更加高效&#xff0c;因为不必将多个独立的视频文件分别处理。最后&#xff0c;合并视频可以减少文件数量&#xff0c;从而使整…

K8S系列文章之 Kind 部署K8S的 服务发布

安装kind 下载 https://github.com/kubernetes-sigs/kind/releases/download/0.17.0/kind-linux-amd64 执行以下命令&#xff1a; mv kind-linux-amd64 /usr/local/bin/kind chmod 777 /usr/local/bin/kind 之前需要先在本地主机安装好docker yum -y install yum-utils d…

vscode Google代码风格设置无效解决

1. 采用第一个方法设置google代码设置风格 2. 安装了clangd后需要在格式化风格做选择 vscode 安装 clang-format插件 $ code /home/tony/.config/Code/User/settings.json 这就能解决google风格设置无效的问题了&#xff0c;原来根因在于使用的格式化插件没有生效导致&#xf…

MemFire教程|FastAPI+MemFire Cloud+LangChain开发ChatGPT应用-Part2

基本介绍 上篇文章我们讲解了使用FastAPIMemFire CloudLangChain进行GPT知识库开发的基本原理和关键路径的代码实现。目前完整的实现代码已经上传到了github&#xff0c;感兴趣的可以自己玩一下&#xff1a; https://github.com/MemFire-Cloud/memfirecloud-qa 目前代码主要…

VIM 编辑器: Bram Moolenaar

VIM 用了很长时间&#xff0c; 个人的 VIM 配置文件差不多10年没有更新了。以前写程序的时候&#xff0c; 编辑都用这个。 linux kernel&#xff0c; boost规模的代码都不在话下。现在虽然代码写的少了&#xff0c;依然是我打开文件的首选。 现在用手机了&#xff0c;配个蓝牙键…

UE中低延时播放RTSP监控视频解决方案

第1章 方案简介 1.1 行业痛点 在各种智慧城市、智慧社区、智慧水利、智慧矿山等数字孪生项目中&#xff0c;经常使用通UE来开发三维可视化场景。在这些场景中通常都需要把现场的各种监控视频在UE的可视化场景中接入&#xff0c;主要包含海康威视、大华、宇视、华为等众多监控…

腾讯云-宝塔添加MySQL数据库

1. 数据库菜单 2. 添加数据库 3. 数据库添加成功 4. 上传数据库文件 5. 导入数据库文件 6. 开启数据库权限 7. 添加安全组 (宝塔/腾讯云) 8. Navicat 连接成功

小白到运维工程师自学之路 第六十五集 (docker-compose)

一、概述 Docker Compose 的前身是 Fig&#xff0c;它是一个定义及运行多个 Docker 容器的工具。可以使用YAML文件来配置应用程序的服务。然后&#xff0c;使用单个命令&#xff0c;您可以创建并启动配置中的所有服务。Docker Compose 会通过解析容器间的依赖关系&#xff08;…