Linux学习笔记:信号

信号

  • 在Linux中什么是信号
  • 信号的产生方式
    • 硬件产生的信号
    • 软件产生的信号
    • 异常产生的信号
  • 进程对信号的处理
    • 信号的保存
    • 信号方法更改函数signal
      • 信号处理的更改
      • 恢复默认
      • 信号忽略
    • 信号的管理
      • 信号集 sigset_t
      • 对信号集的操作
    • 信号的捕捉过程

在Linux中什么是信号

在 Linux 系统中,信号是一种进程间通信的基本机制,用于通知进程发生了某种事件。
信号是一种软件中断,用于通知进程发生了某种事件。
这些事件可能包括硬件异常、用户输入、系统调用请求等。信号是异步发生的,即进程无法预测信号何时发生,但当信号发生时,系统会将信号发送给相应的进程。

在Linux中,信号一般被分为三大类:可以在bash使用kill -l 命令直接产看
在这里插入图片描述

  1. 标准信号(Standard Signals):由内核或进程向进程发送的信号,上图中的1-31号新号即为标准信号

  2. 实时信号(Real-time Signals):在标准信号的基础上引入了实时性概念,允许信号排队和按优先级传递,上图中的34-64即为实时信号

  3. 自定义信号(User-defined Signals):用户可以定义自己的信号类型,用于特定应用或通信需求。

信号的产生方式

无论信号有多少种产生方式,永远只能让OS向目标进程发送

硬件产生的信号

硬件产生的信号是由硬件设备或操作系统内核生成的,用于通知进程发生了某种事件。例如,当我们按下键盘上的^C键时,硬件会生成中断信号(SIGINT)来通知操作系统,然后操作系统将其传递给相应的进程

软件产生的信号

软件产生的信号是由进程自身或其他进程通过系统调用(如 kill )发送给目标进程的。这种方式允许进程之间进行通信和协作,例如向目标进程发送中断信号(SIGINT)以请求其终止执行:kill -9 pidnum

异常产生的信号

异常产生的信号是由硬件或操作系统检测到的异常事件引起的。例如,当进程执行非法指令、访问越界内存或发生浮点数异常时,硬件或操作系统会生成相应的信号(如 SIGILL、SIGSEGV、SIGFPE)来通知进程发生了异常情况。

进程对信号的处理

信号的保存

每个进程的PCB中都有一张自己的函数指针数组,一般被称为信号处理函数表,这个数组中的每个元素对应一个特定信号的处理函数。

当进程接收到信号时,操作系统会根据进程的PCB中的信号处理函数表找到对应的处理函数,并执行相应的逻辑。

以下是对信号处理的几个概念:

  1. 信号递达:实际信号的处理动作,一般有三种:默认,忽略,自定义
  2. 信号未决(pending):信号从产生到递达之间的状态,即在信号的位图中时,一斤产生但未被处理时的状态
  3. 信号阻塞(block):信号时允许被阻塞的,信号产生了,但暂时不进行递达的信号就是阻塞信号

因此操作系统会在进程PCB中创建三个表,用于对信号的状态进行记录,
其中,block表记录的是对应位置信号是否被屏蔽
pending表记录的是对应位置信号是否未决
handler表记录的事对应位置信号的执行方式
在这里插入图片描述

信号方法更改函数signal

信号处理的更改

在Linux中,信号处理函数 (signal) 是一种用来处理异步事件的方法。
信号是一个软件中断,通常由操作系统生成,用来通知程序某个事件已经发生,例如:非法操作、外部中断、定时器溢出等。

原型如下:

void (*signal(int signum, void (*handler)(int)))(int);

两个参数:

  1. signum:表示要设置的信号编号
  2. handler:表示要设置的信号处理函数,可以是一个函数指针,一般来说是我们自己写的一个函数,将signum所表示的信号重新实现

注意事项

  • 可重入性:信号处理函数应该是可重入的,即它们应该避免使用全局状态和执行非原子操作。
  • 限制:在信号处理函数中,只有少数几个函数是安全可调用的(通常称为异步信全函数)。例如,大多数系统调用和库函数都不应该在信号处理函数中调用。

举例:这是一个将2号信号进行更改,原本应该执行的功能被我修改成了handler函数内的内容

#include<iostream>
#include<signal.h>
#include<unistd.h>void handler(int signo)
{std::cout<< "获得一个" << signo << "号信号" << std::endl;exit(1);
}int main()
{signal(2,handler);return 0;
}

恢复默认

如果在开发过程中,忘记了自己之前对某个信号的执行更改,可以将signal函数的第二个参数传入宏:SIG_DFL来使得信号执行它默认的功能

signal(2,SIG_DFL);

信号忽略

在程序开发中,如果想要忽略某个信号,可以直接将signal函数的第二个参数传参宏:SIG_IGN

signal(2,SIG_IGN);

信号的管理

为了高效地处理多个信号,因此要先描述,再组织 , Linux提供了信号集的概念,使得可以将多个信号组合在一起进行处理。

信号集 sigset_t

sigset_t是一个用于表示信号集的数据类型,它通常定义在<signal.h>头文件中。
原型:

typedef struct sigset_t {unsigned long sig[_NSIG / sizeof(long)];
} sigset_t;

其中,_NSIG是一个宏,表示系统中定义的信号总数。sigset_t类型的信号集用于保存一个或多个信号的集合,可以通过位运算来操控信号集。

对信号集的操作

  1. 要创建一个空的信号集,可以使用以下代码:
sigset_t empty_set;
sigemptyset(&empty_set);

这样就有了一个新的信号集,这将清除empty_set中的所有信号位,使其成为一个空集。

  1. 创建一个全满的信号集:
sigset_t full_set;
sigfillset(&full_set);

这样就可以创建一个全满的信号集,并且将full_set中的所有信号位设置为1,表示设置了所有可能的信号。

  1. 设置或查询进程的信号前景,就好像设置权限掩码一样,对信号这些信号在进程处于等待状态(如在 select、poll、epoll_wait 等系统调用中)时会被优先处理:
void sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

其中,how参数表示拷贝的方式,可以是SIG_BLOCK、SIG_UNBLOCK或SIG_SETMASK。set参数指向要拷贝的信号集,oldset参数用于接收旧的信号集。

  1. 添加信号集:
void sigaddset(sigset_t *set, int signum);

该函数将signum信号的位添加到set信号集中。

  1. 清除信号集:
void sigdelset(sigset_t *set, int signum);

该函数将signum信号的位从set信号集中删除。

  1. 检查信号是否存在于信号集中:
int sigismember(const sigset_t *set, int signum);

返回 1:如果指定的信号 signum 在信号集 set 中。
返回 0:如果指定的信号 signum 不在信号集 set 中。
返回 -1:如果发生错误(例如指定的信号编号无效),并且设置全局变量 errno

  1. 查询当前进程等待处理的信号集合,也就是查询处于pending状态的信号集合.这个函数可以帮助进程了解有哪些信号已经被生成但尚未被处理。
int sigpending(sigset_t *set);

当 sigpending 函数被调用时,它会将当前进程的信号等待集合(即那些已经被生成但尚未被处理或阻塞的信号)复制到 set 指向的 sigset_t 变量中。这样,进程就可以通过检查 set 中的信号位来确定有哪些信号需要处理。

下面是一个代码示例,我们首先使用 sigprocmask 函数屏蔽了 SIGINT 信号。然后,我们让进程睡眠5秒钟,以便有足够的时间让用户按下 Ctrl+C。在睡眠之后,我们调用 sigpending 函数来查询当前进程等待的信号。如果 SIGINT 信号在等待队列中,我们将打印出相应的信息。最后,我们使用 sigprocmask 函数恢复到旧的信号掩码,以便进程可以正常处理信号。

#include <signal.h>
#include <unistd.h>
#include <stdio.h>int main() {sigset_t pending_mask, empty_mask, old_mask;// 创建一个信号集合,用于存储 `SIGINT`sigemptyset(&empty_mask);sigaddset(&empty_mask, SIGINT); // 添加 SIGINT 到屏蔽集合// 阻止 SIGINT 信号if (sigprocmask(SIG_BLOCK, &empty_mask, &old_mask) == -1) {perror("sigprocmask");return 1;}printf("SIGINT 信号已被屏蔽,现在我将进入睡眠状态。\n");// 睡眠一段时间,以便有机会生成 SIGINT 信号sleep(5);// 查询当前进程等待的信号if (sigpending(&pending_mask) == -1) {perror("sigpending");return 1;}// 打印等待的信号if (sigismember(&pending_mask, SIGINT)) {printf("SIGINT 信号正在等待处理。\n");} else {printf("没有信号在等待处理。\n");}// 恢复旧的信号掩码if (sigprocmask(SIG_SETMASK, &old_mask, NULL) == -1) {perror("sigprocmask");return 1;}return 0;
}

信号的捕捉过程

如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。
但是自定义信号处理函数是我们自己写的,是存在于用户空间的,因此这里涉及到一个用户到内核的转换问题,因为用户并不具有所有的操作权限,下面的图片可以很清楚的表达出信号捕捉过程中用户和内核态的转化过程
在这里插入图片描述
图片来自必应搜索

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

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

相关文章

MATLAB添加自编写.m文件或.mat数据并永久全局调用方法

菜单栏选择设置路径&#xff0c;然后“添加并包含子文件夹”&#xff0c;在弹出窗口中找到目标文件夹即可。此方案可以永久地将文件夹加入MATLAB路径。 添加包含自编写.m文件的文件夹&#xff1a; 即可实现永久全局调用。

【Elasticsearch运维系列】Elasticsearch7.12.1启动指定版本JDK:你学废了吗?

一、背景 一套生ES集群&#xff0c;版本为7.12.1&#xff0c;近期频繁告警&#xff0c;频繁出现索引分片异常&#xff0c;索引状态异常&#xff0c;导致应用无法正常写入ES&#xff0c;另外&#xff0c;也经常出现节点掉问题。通过分析相关ES日志&#xff0c;显示和当前JAVA G…

vue 开发环境的搭建

一、整个流程&#xff1a; 安装nodejs >> 安装vue >> 安装vue-cli >> 初始化 webpack(生成代码) >> 安装依赖 >> 运行vue程序 二、详细安装流程&#xff1a; 1.安装nodejs 下载&#xff1a;https://nodejs.org/dist/v12.18.3/node-v12.18.3-x…

电商核心内容揭秘50:个性化广告与投放策略

相关系列文章 电商技术揭秘相关系列文章合集&#xff08;1&#xff09; 电商技术揭秘相关系列文章合集&#xff08;2&#xff09; 电商技术揭秘相关系列文章合集&#xff08;3&#xff09; 电商技术揭秘四十一&#xff1a;电商平台的营销系统浅析 电商技术揭秘四十二&#…

基于卷积神经网络的高光谱分类 CNN(上)

基于卷积神经网络的高光谱分类 CNN 混合光谱HybridSN传统的2-D CNN混合光谱3-D CNN 操作步骤前言&#xff08;准备&#xff09;获取数据以及引入基本的库函数导入相关的包 创建模型模型网络结构代码测试 混合光谱HybridSN 传统的2-D CNN 传统的2-D CNN方法在处理HSI时往往只考…

密码口令初步

一&#xff0c;弱口令&#xff08;ctfhub&#xff09; 1.打开环境&#xff0c;发送到bp的instruder板块&#xff0c;一般id默认为admin&#xff0c;也可以用bp找出来&#xff0c;这里就是 2.先clear &#xff0c;再把password等号后面添加进来&#xff08;add&#xff09;&am…

通过 Java 操作 redis -- list 列表基本命令

目录 使用命令 lpush&#xff0c;lrange&#xff0c;rpush 使用命令 lpop 和 rpop 使用命令 blpop&#xff0c;brpop 使用命令 llen 关于 redis list 列表类型的相关命令推荐看Redis - list 列表 要想通过 Java 操作 redis&#xff0c;首先要连接上 redis 服务器&#xff…

记一次java进程频繁挂掉问题排查修复

前言 最近业务部门有个java服务进程会突然无缘无故的挂掉&#xff0c;然后这个服务会产生一堆类似hs_err_pid19287.log这样的日志。业务部门负责人就把hs_err_pidxxx的日志发给我&#xff0c;让我帮忙看下问题。本文就来回顾一下&#xff0c;我是如何帮业务部门进行问题排查 …

一篇文章fpmarkets澳福盘点摇摆交易优缺点

通过之前的文章&#xff0c;我们各位投资者想必都已经明白了什么是摇摆交易&#xff0c;以及摇摆交易的特点和使用方法&#xff0c;今天fpmarkets澳福就一篇文章盘点摇摆交易优缺点&#xff1a; 摇摆交易策略优势: 更有利可图 与趋势策略不同&#xff0c;投资者可能会在摇摆交易…

Beego 使用教程 5:页面视图

beego 是一个用于Go编程语言的开源、高性能的 web 框架 beego 被用于在Go语言中企业应用程序的快速开发&#xff0c;包括RESTful API、web应用程序和后端服务。它的灵感来源于Tornado&#xff0c; Sinatra 和 Flask beego 官网&#xff1a;http://beego.gocn.vip/ 上面的 bee…

Could not resolve placeholder ‘xx.xxx.host’ in value “xxx“问题解决

Could not resolve placeholder ‘xx.xxx.host’ in value "xxx"问题解决 众多原因其中之一 springboot 项目&#xff0c;idea 配置apollo 时&#xff0c;运行指定了配置文件 uat 所以使用本地配置文件启动 时&#xff0c;一直去找uat 配置文件&#xff0c;结果自…

rust使用serde_json转换Value为rust中的数据类型

为了方便转换未知json数据&#xff0c;我们可以使用serde提供的value类型来进行转换&#xff0c;将json字符串转化为Value值&#xff0c;然后可以快速使用get方法来获取值&#xff1a; let json_str r#"{"name": "John","age": 30,"c…

Satellite Communications Symposium(WCSP2022)

1.Power Allocation for NOMA-Assisted Integrated Satellite-Aerial-Terrestrial Networks with Practical Constraints(具有实际约束的 NOMA 辅助天地一体化网络的功率分配) 摘要&#xff1a;天地一体化网络和非正交多址接入被认为是下一代网络的关键组成部分&#xff0c;为…

Git === Git概述 Git安装

第1章 Git概述 Git是一个免费的、开源的分布式版本控制系统&#xff0c;可以快速高效地处理从小型到大型的各种项目。 Git易于学习&#xff0c;占地面积小&#xff0c;性能极快。 它具有廉价的本地库&#xff0c;方便的暂存区域和多个工作流分支等特性。其性能优于Subversion…

Webshell绕过技巧分析之-base64/HEX/Reverse/Html/Inflate/Rot13

在网络安全运营&#xff0c;护网HVV&#xff0c;重保等活动的过程中&#xff0c;webshell是一个无法绕过的话题。通常出现的webshell都不是以明文的形式出现&#xff0c;而是针对webshell关键的内容进行混淆&#xff0c;编码来绕过网络安全产品&#xff08;IDS&#xff0c;WAF&…

YOLO系列自研改进:基于注意力机制的多尺度特征提取模块

目录 一、原理 二、代码 三、在YOLO中的应用 一、原理 这个模块的原理仍然是利用不同大小的卷积核来提取不同尺度的特征,同样将通道划分为两部分,一部分通过注意力机制进行通道信息和空间信息的提取,另一部分通过多个不同大小的卷积核来提取多尺度的特征信息。 二、代码…

Baidu Comate——AI时代的软件开发利器

目录 Comate产品介绍 1.产品背景 ​编辑 2.产品优势 3.产品特性 4. 支持开发环境及语言 5.使用场景 Comate产品体验 Comate场景应用 2.快捷键的使用 专业插件体验 1.行间注释 2. 代码优化 3.解释说明代码 4.调优建议 5.AutoWork Comate实测体验感受 Comate产品介绍…

Java 8特性(一) 之 手写Stream流filter、map和forEach方法

Java 8特性&#xff08;一&#xff09; 之 手写Stream流filter、map和forEach方法 今天看了一下Java 8的Stream流&#xff0c;学习了一下函数式编程&#xff0c;这才感受函数式编程如此爽&#xff0c;之前就使用过ES8.7.1的函数式编程&#xff0c;当时就在想啥时候咱也能写出这…

Java的BIO/NIO/AIO

1. Java中的BIO、NIO和AIO的基本概念及其主要区别 BIO (Blocking I/O): 传统的同步阻塞I/O模型。每个连接创建成功后都需要一个线程来处理&#xff0c;如果连接没有数据可读&#xff0c;则线程会阻塞在读操作上。这种模型简单易理解&#xff0c;但在高并发环境下会消耗大量系统…

ORACLE 19C RAC DIAG进程消耗大量内存的分析

近期一个ORACLE 19C的RAC环境&#xff0c;多次出现数据库实例的后台进程DIAG消耗很多内存&#xff08;达到20G&#xff09;&#xff0c;节点1、节点2都出现过次问题。 问题分析&#xff1a;通过对DIAG进程TRACE分析&#xff0c;结合在ORACLE官方后台进行问题、BUG查询匹配&…