Linux下的进程信号

news2024/9/27 12:16:55

目录

信号背景: 

信号产生前

Core Dump

信号产生中

信号产生后

其他概念 

不可重入函数 

volatile关键字

SIGCHLD 17号信号


 

信号背景: 

        在生活中处处都存在的信号,比如信号灯,要想处理信号,我们就必须具备两种条件,首先是能够识别信号,第二个是处理信号的能力,也就知道如何处理信号,对于一个进程来说也是如此,必须提前满足两个条件,第一个就是要能够识别信号,第二个就是知道如何处理信号。

那么进程是如何记住信号的呢,即信号有没有产生,什么信号产生?

内核数据结构使用位图来标记各个信号是否产生,这个位图只能由操作系统去修改。

对于信号的处理有三种方式:
第一种是忽略,第二种是默认的处理方式是由操作系统提供,最后一种是用户自定义的信号处理方式。(注意忽略也算作已对信号的处理,不过是什么也不做)

        信号对于进程的控制流程来说是异步的,进程运行中随时可能产生,所以对于进程来说,它可能在控制流程的任何一个地方产生信号。

使用kill -l查看所有信号列表:

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

对于进程识别和处理信号而言,将其分成三个阶段来理解,为信号产生前和信号产生中,信号产生后。

信号产生前

首先是信号产生前,信号产生前谈的是信号的产生方式,主要有四种:

首先是通过终端按键,比如:

ctrl+c 发送SIGINT 2号信号给进程

ctrl+\ 发送SIGQUIT 3号信号给进程

可以通过信号捕捉函数signal进行代码验证,验证发现九号信号是不可以捕捉的,进程收到9号信号会直接退出:

 测试:

使用命令行kill -信号编号/信号名给信号发信号:

kill命令其实就是进程产生信号的第二种方式即使用系统调用,kill程序的实现就是调用系统调用kill函数,使用kill函数给指定进程发送信号。

 使用库函数调用abort函数给当前进程发送SIGABRT信号终止进程。

还可以使用raise函数给当前进程发送指定信号。

第三种产生信号的方式是软件条件产生,调用alarm函数可以像定闹钟一样,当时间到了给进程发送SIGALRM信号:

OS是也一款软件,一启动就不停息地运行,那么是谁在推动操作系统做一系列的事情呢?是硬件,硬件不断给操作系统发送时钟中断,让操作系统不断自然运行。

还有SIGPIPE

还一种产生信号的方式是硬件异常产生,比如说数组越界访问,空指针解引用等问题,操作系统会给进程发送指定的信号,标识进程运行中出现了错误,中断进程。

在数组越界的时候,访问指定元素的地址,通过页表进行虚拟地址转化到物理地址时,这个过程主要是由软件页表和硬件MMU内存管理单元完成的工作,当进行地址转化的过程中,内存管理单元出现错误,是一种硬件异常,操作系统就能够识别到硬件的异常,给指定的进程发送指定的信号。

数组越界进程收到信号终止,其实是收到了SIGSEGV信号,表示段错误。

比如除零错误cpu内部有状态寄存器,当进行运算时出现错误状态,寄存器就会发生变化,记录错误操作系统,同时也能够识别到这个错误,给进程发送SIGFPE信号,表示浮点数异常。

 即使是硬件异常也不一定会终止进程,这取决于用户是否自定义捕捉了这个信号给这个信号自定义处理方法来决定的。

Core Dump

使用man 7 signal命令可以在手册中可以看到不同的信号,在处理的过程中,有一些是中断,有一些会产生核心转储文件:

这个核心转储文件,本质上就是进程,在运行的过程中出现了异常,操作系统将进程运行的上下文数据保存在磁盘文件中,这个文件就是核心转储文件,使用GCC的-g选项以debug模式编译程序,程序产生的核心转储文件才可以通过gdb调试,通过gdb调试可以定位到代码某一行发生错误。

实际运用中,因为核心转储这个功能是要占据许多磁盘空间的,每一次发生错误都会产生一个核心转储文件,但并不是能够及时的排查错误,所以在应用中通常会关闭这个选项,让进程在说到信号是不产生核心转储文件。

使用ulimit -a可以查看core文件大小:

可以看到默认的core文件大小限定为0,意味着不生产core文件,使用ulimit -c 文件大小 可以改变core文件大小,从而使程序生成core文件。

信号产生中

第二个信号的阶段是信号产生中。

需要引入两个概念,首先是信号的递达,指的是在处理信号的时候做的动作。第二个是信号的未决,表示信号从产生到信号递达之前的状态叫做信号处于未决状态。

信号的阻塞表示让信号保持在未决状态,直到解除对信号的阻塞为止。

操作系统内部实现有三张表来对信号的处理而产生进行管理,首先是阻塞信号,阻塞指定信号不能防止收到对应信号,但是能阻止信号的抵达,而且当信号被阻塞时,进程多次收到相同信号,但只会被抵达一次,因为内部收到信号的标识位是由位图实现的,第二个表是pending表,它是表示进程是否收到信号的位图,最后handler表是是一个函数指针数组,自定义信号处理方法的本质上就是修改函数指针数组内的函数指针变量。

源代码中可以看到三张表在task_struct结构体中:

画图理解:

那么如何操作这三张表?

通过signal函数可以捕获信号,并为该信号自定义处理函数,还可以通过调用sigaction函数自定义信号的处理方法:

 其中参数类型struct sigaction具有如下成员,包含成员sa_handler函数指针变量,通过创建并修改结构体的该成员,再调用sigaction将其设置进内核中即可。

 比如要为2号信号自定义处理方法:

void handler(int sig)
{
    //....
}
int main()
{
    struct sigaction act, oact;
    //可以自定义处理方法,也可以用系统提供的宏将处理动作设置为忽略(无行为),或默认(进程退出)
     act.sa_handler = handler;
    // act.sa_handler = SIG_IGN;
    // act.sa_handler = SIG_DFL;
    act.sa_flags = 0;
    sigemptyset(&act.sa_mask);
    sigaction(2, &act, &oact);//将原本的sigaction拷贝进oact中,可供恢复
    
}

对block和pending表的描述,OS统一使用sigset_t结构体,修改block表只要创建并操作sigset_t结构体然后调用sigprocmask修改阻塞信号集(即block表又称信号屏蔽字)即可。

sigprocmask函数,第一个参数传入宏

第一个参数宏

SIG_SETMASK 表示要设置阻塞信号集,将原信号集直接设置成我们传入的信号集set

SIG_UNLOCK 表示解除信号屏蔽字,将原信号集中含有我们传入的set中的比特位移除

SIG_BLOCK 表示添加到当前信号屏蔽字的信号,相当于原信号集或上我们的set

 sigprocmask函数,第二个参数传入要设置的信号集,第三参数表示将原来的信号屏蔽字备份到oset里。

要读取pending表使用sigpending函数即可,该函数的参数就是输出型参数。

要操作sigset_t结构体使用信号集操作函数:

#include <signal.h>

int sigemptyset(sigset_t *set);清空信号集,将所有比特位置0

int sigfillset(sigset_t *set);将信号集所有比特位置1

int sigaddset(sigset_t *set, int signum);添加signum信号到信号集,将对应比特位置1

int sigdelset(sigset_t *set, int signum);删除信号集中的signum信号,将对应比特位置0

int sigismember(const sigset_t *set, int signum);  判断信号集是否包含signum信号(比特位是否为1)

 注意:当进程在处理信号的函数中并同时收到信号时,该信号会被阻塞。

信号产生后

对于4G的内存而言,地址空间被划分成用户地址空间和内核地址空间,其通过用户级页表和内核级页表分别与物理地址建立映射。

用户级页表:0-3G 每一个进程都有一份用户级页表,大家的页表都不同

内核级页表:3-4G:所有进程共享,所以无论进程如何切换,都能找到内核的代码和数据

可见每个进程都能看到OS,但是并不是进程随时能进行访问的,需要具备访问的权利。如何具备访问的权利,访问内核数据?

需要以内核态的身份才可以访问。在CPU中的CR3寄存器中有比特位标识进程状态处于内核态还是用户态,状态区别在于访问权限不同。

用户态 :只能访问用户级页表

内核态:能访问内核和用户页表

何时会切换成内核态呢?

执行系统调用或时间片到了(中断)或异常,进程间切换时会身份切换更改成内核态。 

因为pending表和block表是内核的数据结构,修改其只有内核态才有权限,所以在内核态返回用户态前OS便顺便对进程PCB中pending和block表进行检查。

如果发现有信号产生且信号未被阻塞 ,就查看handler表,如果不是用户自定义的处理方法在内核态执行其方法就可以了,如果是用户自定义处理方法 ,就切换回用户态,切换会用户态是因为要保护OS,防止恶意代码利用内核态的身份做本无权限做的事情。在执行自定义方法处理后,再返回内核态,不直接返回用户态是因为比如系统调用时需要准备函数的返回值等,最后再切换回用户态 。

画图理解:

 

简图快速记忆方法:无穷大,一线4交点

 

其他概念 

不可重入函数 

重入:被多个执行流重复进入

不可重入函数:对于重入时导致发生访问全局变量造成错误的函数。

反之, 如果一个函数只访问自己的局部变量或参数,则称为可重入函数。

当符合下面条件之一的函数是不可重入的:

调用了malloc,free函数

调用了IO库函数,标准IO库的实现常以全局数据结构的不可重入方式实现

举例理解不可重入函数:

 

volatile关键字

volatile为c语言关键字,意为保持内存可见性。

正常情况下从内存读取数据到CPU,但在编译器的高优化级别下变量会被优化到寄存器中 

使用volatile关键字声明变量,就相当于告诉编译器不对变量做任何优化,每次CPU拿内存中的数据都必须从内存中读取,即为保持内存可见性。

为何需要这个特性呢?因为在高优化级别下,有时候访问变量,因为变量被优化到寄存器,所以变量在内存的修改不能马上同步到寄存器上,这时访问寄存器上的值就和内存中不同,就照成了访问数据出错的问题。

SIGCHLD 17号信号

子进程退出或暂停或继续时会给父进程发送SICHLD信号 ,该信号的处理动作是SIG_IGN,也就是什么都不做的忽略动作。

在命令行使用kill -19 pid给进程发送暂停信号,kill -18 pid给进程发送继续信号。

SIGCHLD信号用途

之前提到子进程退出,父进程不等待,会导致子进程变成僵尸进程照成内存泄漏。为解决僵尸状态问题可以利用这个信号。父进程捕捉SIGCHLD信号,在信号处理函数中等待子进程即可。

捕获信号可以使用signal或sigaction函数,使用signal函数时,将SIGCHLD设置成忽略SIG_IGN,本来SIGCHLD信号的处理动作就是SIG_IGN,看起来没区别,但是设置之后子进程终止时会自动释放不会进入僵尸状态,这时Linux下的特例,在其他平台不保证如此行为。

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

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

相关文章

day26-XML/枚举/注解

1.xml 1.1概述【理解】 万维网联盟(W3C) 万维网联盟(W3C)创建于1994年&#xff0c;又称W3C理事会。1994年10月在麻省理工学院计算机科学实验室成立。 建立者&#xff1a; Tim Berners-Lee (蒂姆伯纳斯李)。 是Web技术领域最具权威和影响力的国际中立性技术标准机构。 到目前为…

AWS - IAM

AWS IAM 自用笔记。 Terms IAM - Identity and Access Management, a global service it gives: Centralized control of AWS accountShared access to AWS accountGranular permissionsIdentity Federation, i.e., Facebook, LinkedIn etc. it allows: Multi-Factor Authe…

20230121解决AIO-3568J开发板无法刷机以及串口异常的问题

20230121解决AIO-3568J开发板无法刷机以及串口异常的问题 2023/1/21 22:31 一片比较旧的AIO-3568J开发板&#xff0c;症状&#xff1a; 1、无法刷机&启动。【Loader模式 & MASKROM模式】 2、串口输出乱码&#xff01; &#xff08;一&#xff09;刷机问题的解决&#x…

一起自学SLAM算法:7.3 估计理论

连载文章&#xff0c;长期更新&#xff0c;欢迎关注&#xff1a; 不管是用贝叶斯网络还是因子图&#xff0c;一旦SLAM问题用概率图模型得到表示后&#xff0c;接下来就是利用可观测量&#xff08;和&#xff09;推理不可观测量&#xff08;和&#xff09;&#xff0c;也就是说S…

初识C语言(上)

写在前面 我们正式开始接触到C语言,这是我在学习过C语言后重新写的博客,我把之前的稍微优化了一下,希望能用更加朴素的语言和大家分享,我希望给大家带来一个可以看的懂,理论和实践并行的内容.当然里面也会存在一些错误和不恰当的地方,还请诸位指正. 为何学习C语言 我想从两个…

mac 疑难问题汇总

macos 更改zsh到bash查看当前系统有哪些bash命令行&#xff1a;cat /etc/shells切换成bash命令行&#xff1a;chsh -s /bin/bashmac触摸屏轻点设置Mac通过crontab设置定时任务报错Operation not permitted1、系统偏好设置->安全性和隐私->完全磁盘访问权限2、解除锁定允许…

【双向链表】java代码实现

前言&#xff1a; 大家好&#xff0c;我是良辰丫&#x1f353;&#x1f353;&#x1f353;&#xff0c;上一篇文章我和大家一起去探索了单链表的知识世界&#xff0c;今天我们去接触双向链表&#xff0c;什么&#xff1f;没听错&#xff0c;就是双向链表&#xff0c;比单链表更…

UE INI文件操作 INI File Operation [ Read / Write ] 插件说明

在 Windows 平台上的 INI 文件读写操作 1. Write INI String 输入&#xff1a; Directory&#xff1a; 选择保存目录 Project Directory &#xff1a; 当前项目目录Project Content Directory&#xff1a;当前项目 Content 目录Project Config Directory&#xff1a;当前项目…

JVM内存区域的划分

根据 JVM 规范&#xff0c;JVM 内存共分为虚拟机栈、堆、方法区、程序计数器、本地方法栈五个部分。 程序计数器&#xff08;线程私有&#xff09;&#xff1a; 是当前线程所执行的字节码的行号指示器&#xff0c;每条线程都要有一个独立的程序计数器&#xff0c;这类内存也称为…

Java基础08:面向对象进阶(上)

Java基础08&#xff1a;面向对象进阶&#xff08;上&#xff09;一、static1. 静态变量2. 静态方法3. 工具类4. static注意事项5. 重新认识main方法二、继承1. 继承的概念2. 继承的特点3. 继承中访问成员变量4. 继承中访问成员方法&#xff08;方法重写&#xff09;5. 继承中访…

给大家准备了程序员专属红包封面,审核通过后我哭了,太心酸了(┬_┬)

大家好&#xff0c;我是小悟 今天就是除夕了&#xff0c;也就是大年三十&#xff0c;小伙伴们应该都回家了吧&#xff0c;小悟祝大家新年快乐&#xff0c;身体健康&#xff0c;万事如意&#xff0c;兔飞猛进哦。 春节临近&#xff0c;收到微信定制红包封面并送了四千个名额的…

【深度学习】详解 MoCo

目录 摘要 一、引言 二、相关工作 三、方法 3.1 Contrastive Learning as Dictionary Look-up 3.2 Momentum Contrast 3.3 Pretext Task 四、实验 4.1 Linear Classification Protocol 总结 ☆ 实现 参考资料 Title&#xff1a;Momentum Contrast for Unsupervised…

AlmaLinux 9 安装Kasm Workspaces

今天尝试一下AlmaLinux 9 安装Kasm Workspaces。 前提条件 安装了Docker和Docker Compose&#xff0c;已经最新版本要求&#xff0c; docker 18.06 docker compose 2.1.1 创建一个Swap分区 下面的步骤将创建一个2千兆字节&#xff08;2048MB&#xff09;的交换分区。请根据…

我的创作纪念日——“永远相信美好的事情即将发生”

作者&#xff1a;非妃是公主 专栏&#xff1a;《程序人生》 个性签&#xff1a;顺境不惰&#xff0c;逆境不馁&#xff0c;以心制境&#xff0c;万事可成。——曾国藩 文章目录序与CSDN的往事机缘收获憧憬碎碎念序 第一次写创作纪念日的文章&#xff01;哈哈哈哈&#xff0c;今…

一起自学SLAM算法:7.5 基于因子图的状态估计

连载文章&#xff0c;长期更新&#xff0c;欢迎关注&#xff1a; 虽然式&#xff08;7-90&#xff09;所示的完全SLAM系统可以用滤波方法求解&#xff0c;比如著名的Fast-SLAM实现框架。但是&#xff0c;贝叶斯网络表示下的完全SLAM系统能很方面地转换成因子图表示&#xff0c;…

字符串匹配: BF与KMP算法

文章目录一. BF算法1. 算法思想2. 代码实现二. KMP算法1. 算法思想概述2. 理解基于最长相等前后缀进行匹配3. 代码中如何实现next数组5. 代码实现6. next数组的优化一. BF算法 1. 算法思想 BF 算法, 即暴力(Brute Force)算法, 是普通的模式匹配算法, 假设现在我们面临这样一个…

24/365 java 观测线程状态 线程优先级

1.观测线程 JDK中定义的线程的六个状态 &#xff1a; 可以用getState()来观测线程 public static void main(String[] args) throws InterruptedException {Thread thread new Thread(()->{for (int i 0; i < 10; i) {try {Thread.sleep(100);} catch (InterruptedExc…

2023适合新手的免费编曲软件FL Studio水果21中文版

水果软件即FL Studio&#xff0c;这是一款较为专业的编曲软件&#xff0c;这款软件自带高品质打击乐、钢琴、弦乐以及吉他等107种乐器效果&#xff0c;内置了包括经典电子音色、合成利器3xosc、sytrus、slicex等多种插件&#xff0c;可以帮助音乐制作人创作不同的音乐曲风&…

数据结构进阶 哈希表

作者&#xff1a;小萌新 专栏&#xff1a;数据结构进阶 作者简介&#xff1a;大二学生 希望能和大家一起进步&#xff01; 本篇博客简介&#xff1a;模拟实现高阶数据结构 哈希表 哈希表 哈希桶哈希概念举例哈希冲突哈希函数哈希冲突的解决方式之一闭散列 --开放定址法哈希表的…

Python CalmAn(Calcium Imaging Analysis)神经生物学工具包安装及环境配置过程

文章目录CalmAn简介安装要求我的设备1>CalmAn压缩包解压&#xff08;caiman文件夹要改名&#xff09;2>conda创建虚拟环境3>requirements依赖包配置&#xff08;包括tensorflow&#xff09;4>caiman安装(mamba install)5>caimanmanager.py install6>PyCharm添…