Linux信号处理浅析

一、信号从发送到被处理经历的过程

1、常见概念

(1) 信号阻塞

阻塞,即被进程拉黑,信号被发送后,分为两种情况,一种是被阻塞了(被拉黑了),一种是没有被阻塞。

(2) 信号未决

在信号被进程处理之前的过程,都可以称为信号未决

(3) 信号递达

信号被进程处理,我们称为信号递达

(4) 信号忽略

进程收到信号后直接忽略,不进行处理,是否还有默认处理呢?(忽略中断信号,验证是否会中断)

(5) 阻塞信号集

指一个进程中当前阻塞而不能够递送给该进程的信号集

(6) 未决信号集

指当前进程未处理的信号集,收到阻塞的信号,未决信号集该信号位会置1,解除阻塞后会置0。

2、信号保存的方式(内核级)

block表:对应的信号是否被阻塞,0表示不阻塞,1表示阻塞

pending表信号是否被收到,0表示未收到该信号,1表示收到该信号

handler表:表示对应信号的处理方式(函数),存放的是函数的地址(函数指针)

3、信号从发送到被处理

第一列表示第 N 号信号,以第二行为例,阻塞信号为 1 ,说明该信号被阻塞了(相当于在进程的黑名单里),后面的未决信号是0还是1都无所谓了

以第四行为例,阻塞信号为0,该信号没有被阻塞,未决信号为1,说明收到这个信号了,对应的处理方式是 sighandler函数,那就会执行该函数

注意:

SIG_DFL(signal default):信号默认处理方式

SIG_IGN(signal ignore):忽略信号
 

总结

先看block,如果block为1,即信号被拉黑,是否收到信号都不重要了

如果block不为1,那就再看pending是否为1,即是否收到信号

二、信号集操作(信号保存)

阻塞信号其实就是要修改block表,但是要怎么修改呢?如果只是传递单个信号,或许可以直接以某种方式告诉OS我希望阻塞哪个信号,但是,如果要修改多个信号呢??OS给出了一种方案来解决传递单个或者多个信号,那就是直接传递位图!!

收到一个信号之后执行信号处理函数,在执行信号处理函数的过程中如果又来了一个相同的信号,那么这个信号将会被阻塞,直到信号处理函数执行完之后,再响应被阻塞的信号,注意如果信号被阻塞期间又收到了该信号,那么多个信号的处理会被合并为1次。

信号集就是用来记录当前收到了哪个信号,会把当前信号的标志位置成“正在处理”,如果此时再收到该信号,那么信号就阻塞等待,使用数据类型sigset_t表示信号集,在Linux中该类型是一个32位无符号整数,这是因为在Linux中定义了32种信号,每一个信号用32位无符号整型变量中的一位来标志,如果该位置为1,那么表示正在处理该信号,如果置为0表示可以处理该信号。

未决信号集保存当前为处理的信号集。

信号屏蔽字又叫阻塞信号集,是指一个进程中当前阻塞而不能够递送给该进程的信号集。每个进程都有一个信号屏蔽字,它规定了当前要阻塞递达到该进程的信号集

1、特定的数据类型sigset_t

既然要传递位图,要用什么来表示位图呢?第一想法是int/uint32_t,不同的操作系统,实现位图的方式可能有所不同,Linux操作系统不光给我们提供了 保存位图的数据类型 sigset_t,还有对应的函数来操作位图(下面说)

——》sigset_t 可以看作是一个信号集,保存着1~31号信号的状态,0表示没收到,1表示收到了

2、操作信号集的函数

(1) sigemptyset函数,清空信号集,全部位置置0。GUN C库中的非标准函数

int  sigemptyset ( sigset_t  *set);

成功返回0,失败返回-1

(2) sigfillset函数:初始化信号集,全部置1。GUN C库中的非标准函数

int  sigfillset( sigset_t  *set);

成功返回0,失败返回-1

(3) sigaddset函数:向信号集中添加一个信号,将对应信号置1。标准函数

假设一开始所有的比特位都是 0(0000 0000 ...),如果我们希望把第二个信号阻塞,那么就需要先把信号集的第二个比特位设置为1(0100 0000....),然后再传给信号阻塞函数

int  sigaddset( sigset_t  *set, int  signum);

 第一个参数是传入信号集的地址,第二个参数是要把哪个位置(信号)设置成1,成功返回0,失败返回-1

(4) sigdelset函数:从信号集中删除一个信号,将对应信号置0。标准函数

(假设前面已经阻塞了2号信号)这个时候的比特位显示为0100 0000....,现在我们不希望阻塞2号信号,那么需要把第二个位置(信号)设置为 0 (0000 0000....)

int  sigdelset( sigset_t  *set, int  signum);

 第一个参数是传入信号集的地址,第二个参数是要把哪个位置(信号)设置成1,成功返回0,失败返回-1

(5) sigismember函数:判断某个信号是否为信号集的成员(判断是否在信号集中)。标准函数

如果对应位置(信号)的比特位为1,说明在信号集中,如果为0,说明不在信号集中

int  sigismember( sigset_t  *set, int  signum);

成功返回1,失败返回0

如果使用sigemptyset()初始化信号集(信号集的所有位置0),此时可以接收到所有的信号

如果使用sigfillset()初始化信号集(信号集的所有位置1),此时屏蔽所有信号,当然SIGKILL和SIGQUIT信号是不能屏蔽的

sigaddset()和sigdelset()的意义在于指定屏蔽某信号或者接收某信号
 

int  sigprocmask (int  how,  const  sigset_t  *restrict set,  sigset_t  *restrict oset);

how取值有下列三个:

SIG_BLOCK :set中包含的是希望阻塞的附加信号,合并set中的信号集

SIG_UNBLOCK:set中包含的是希望解除阻塞的信号,删除set中的信号集

SIG_SETMASK:set中包含的是现有屏蔽字的代替值,替换set中的信号集

若oset是非空指针,则返回进程的当前信号屏蔽字。

若set为空,则进程信号屏蔽字不变,how值无意义。

另外,注意一点,不可以阻塞SIGKILL和SIGSTOP信号。

注意:调用该接口将信号设置为非阻塞时,若该信号已经是阻塞状态,则在该函数返回前马上会发给该线程

int  sigpending ( sigset_t  *set );

此函数通过set返回当前检查未决的信号集。其中的各个信号对于调用进程是阻塞的而不能递送,因而也一定是当前未决的。

signal 函数的使用方法简单,但并不属于 POSIX 标准,在各类 UNIX 平台上的实现不尽相同,因此其用途受到了一定的限制。而 POSIX 标准定义的信号处理接口是 sigaction 函数。

int  sigaction ( int  signum,  const struct  sigaction  *act, struct  sigaction  *oldact ) ;

signum:要操作的信号。

act:要设置的对信号的新处理方式。

oldact:原来对信号的处理方式。

返回值:0 表示成功,-1 表示有错误发生。

struct  sigaction
{
        void (*sa_handler)(int);
        void (*sa_sigaction)(int,  siginfo_t *,  void*);
        sigset_t  sa_mask;
        int  sa_flags;
        void (*sa_restorer)(void);
};

这个结构体中,成员 sa_handler 是一个函数指针,其含义与 signal 函数中的信号处理函数类似。成员sa_sigaction 则是另一个信号处理函数,它有三个参数,可以获得关于信号的更详细的信息。当 sa_flags 成员的值包含了 SA_SIGINFO 标志时,系统将使用 sa_sigaction 函数作为信号处理函数,否则使用 sa_handler 作为信号处理函数。在某些系统中,成员 sa_handler 与 sa_sigaction 被放在联合体中,因此使用时不要同时设置。

sa_mask 用来指定在信号处理函数执行期间需要被屏蔽的信号,特别是当某个信号被处理时,它自身会被自动放入进程的信号掩码,因此在信号处理函数执行期间这个信号不会再度发生。

sa_flags 用于指定信号处理的行为,它可以是一下值的“按位或”组合。

◆ SA_RESTART:如果信号中断了进程的某个系统调用,则系统自动启动该系统调用
◆ SA_NOCLDSTOP:使父进程在它的子进程暂停或继续运行时不会收到 SIGCHLD 信号。
◆ SA_NOCLDWAIT:使父进程在它的子进程退出时不会收到 SIGCHLD 信号,这时子进程如果退出也不会成为僵尸进程。
◆ SA_NODEFER:一般情况下,当信号处理函数运行时,内核将阻塞该给定信号。但是如果设置了 SA_NODEFER标记,那么在该信号处理函数运行时,内核将不会阻塞该信号。
◆ SA_RESETHAND:当调用信号处理函数时,将信号的处理函数重置为缺省值SIG_DFL。
◆ SA_SIGINFO:使用 sa_sigaction 成员而不是 sa_handler 作为信号处理函数。

re_restorer 是一个已经废弃的数据域,不要使用。

可重入函数

一个函数被重入,表示这个函数没有执行完成,由于外部因素或内部调用,又一次进入该函数执行。一个函数要被重入,只有两种情况:

1、多线程同时执行这个函数

2、函数自身(可能经过多层调用之后)调用自身

一个函数可重入,表示这个函数被重入后不会产生任何不良后果。要成人可重入函数必须有以下特定:

1、不使用任何(局部)静态或全局的非const变量

2、不返回任何(局部)静态或全局的非const变量的指针

3、依赖调用方提供参数

4、不依赖任何单个资源的锁

5、不调用任何不可重入的函数

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

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

相关文章

AI教我学编程之C#入门程序详解与拓展

与AI肩并肩 前言一、一个简单的C#程序补充说明对话AI 二、标识符三、关键字四、Main五、空白1. 缩进2. 代码块的间距3. 操作符的空格4. 换行5. 一致性 六、语句七、从程序输出文本主题:从程序中输出文本1. Write 和 WriteLine 方法2. 格式字符串3. 多重标记和值4. 格…

TinyLog iOS v3.0接入文档

1.背景 为在线教育部提供高效、安全、易用的日志组件。 2.功能介绍 2.1 日志格式化 目前输出的日志格式如下: 日志级别/[YYYY-MM-DD HH:MM:SS MS] TinyLog-Tag: |线程| 代码文件名:行数|函数名|日志输出内容触发flush到文件的时机: 每15分钟定时触发…

CVE-2023-51385 OpenSSH ProxyCommand命令注入漏洞

一、背景介绍 ProxyCommand 是 OpenSSH ssh_config 文件中的一个配置选项,它允许通过代理服务器建立 SSH 连接,从而在没有直接网络访问权限的情况下访问目标服务器。这对于需要经过跳板机、堡垒机或代理服务器才能访问的目标主机非常有用。 二、漏洞简…

面试算法108:单词演变

题目 输入两个长度相同但内容不同的单词(beginWord和endWord)和一个单词列表,求从beginWord到endWord的演变序列的最短长度,要求每步只能改变单词中的一个字母,并且演变过程中每步得到的单词都必须在给定的单词列表中…

算法通关村番外篇-LeetCode编程从0到1系列六

大家好我是苏麟 , 今天带来LeetCode编程从0到1系列六 . 链表相关的题目 , 也是面试热题 . 大纲 21. 合并两个有序链表206. 反转链表 21. 合并两个有序链表 描述 : 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 题目 : Le…

Jetson Orin AGX 64GB更新 Jetpack6.0

Jetson Orin AGX 64GB更新 Jetpack6.0 注意: 1,如果你要向我一样为AGX更新Jetpack6.0的话,它还要求你的ubuntu版本必须是20.04 或22.04 2,安装完SDKmanager后,然后选择对应的设备,根据个人选择勾选是否安装…

深入浅出谈Python机器学习的概念及教学

机器学习学习起来并不复杂,下面我用简单的语言说一说: 1.机器学习的分类 常见的机器学习包括有监督和无监督的学习,有监督学习就是用一堆特征变量(也可以理解为解释变量、因子、自变量)去预测响应变量(也…

强化学习10——免模型控制Q-learning算法

Q-learning算法 主要思路 由于 V π ( s ) ∑ a ∈ A π ( a ∣ s ) Q π ( s , a ) V_\pi(s)\sum_{a\in A}\pi(a\mid s)Q_\pi(s,a) Vπ​(s)∑a∈A​π(a∣s)Qπ​(s,a) ,当我们直接预测动作价值函数,在决策中选择Q值最大即动作价值最大的动作&…

RT-Thread基于AT32单片机的CAN应用

1 硬件电路 2 RT-Thread驱动配置 RT-Studio中没有CAN相关的图形配置,需要手动修改board.h。在board.h的末尾,增加相关的BSP配置。 #define RT_CAN_USING_HDR #define BSP_USING_CAN13 IO配置 at32_msp.c中的IO配置是PB9和PB10,掌上实验室V…

JIRA新BUG单浏览器通知

以下代码请放在油猴内使用: // UserScript // name JIRA未处理任务通知 // namespace https://blog.csdn.net/weixin_43515759 // version 1.0 // description Polls an API endpoint and sends a notification if conditions are met // author …

ChatGPT付费创作系统V2.5.5独立版+前端

ChatGPT付费创作系统V2.5.5版本优化了很多细节,功能增加增加长篇写作功能。该版本为编译版无开源,本版本特别处理了后台弹窗、暗链网址。特别优化了数据库。升级过程中未发现任何BUG,全新安装或者升级安装均未出现400或者500错误,…

高级JavaScript中最有趣的原型、原型链?

封装、继承、多态 基于类 class,JavaScript没有类;JavaScript可以实现面向对象语言特征:封装、继承、多态 封装:通俗的来说就是封装函数,通过私有化的变量和私有化的方法,不让外部访问到 继承&#xff1…