【Linux操作系统】:信号

news2025/4/18 1:38:56

Linux操作系统下的信号

在这里插入图片描述

一、引言

首先我们可以简单理解一下信号的概念,信号,顾名思义,就是我们操作系统发送给进程的消息。举个简单的例子,我们在写C/C++程序的时候,当执行a / 0类似的操作的时候,程序直接就挂了,这是什么原因呢?其实本质就是CPU在计算的时候出现异常,触发硬件中断,然后我们操作系统发送了对应的信号给进程,然后我们进程默认就退出了。这一系列东西,我都将在后续的文章中具体的提到。

二、Linux下信号的形式

管他知不知道了不了解,我们都可以先看看Linux下的信号具体长什么样
在这里插入图片描述
在这里插入图片描述
这里我简单的介绍几种常见的信号

  • SIGINT : 这个信号就是我们常用ctrl + c,他默认的作用就是直接将我们的进程退出。
  • SIGSEGV:这个就是段错误的信号,比如我们常见的越界,野指针都是这个错误。
  • SIGCHLD:子进程退出的时候,我们父进程可以hang住,也就是阻塞等待,也可以轮询非阻塞等待,或者也可以通过这个信号,所以这个信号就是子进程退出的的时候发送给我们父进程的信号。
  • SIGKILL:这个信号可以杀死大部分的进程(D状态进程除外),后续我们也会讲解。

三、信号的产生

  1. 由内核产生
    内核在检测到系统事件或异常时会向进程发送信号,例如:
  • 硬件异常:如进程访问非法内存(SIGSEGV)、除零错误(SIGFPE)等。

  • 资源限制:进程占用资源超限(如 SIGXCPU 表示CPU时间超限)。

  • 子进程状态变化:子进程终止时会向父进程发送 SIGCHLD。

  • 终端事件:如终端关闭时发送 SIGHUP,用户按下 Ctrl+C 触发 SIGINT。

  1. 由其他进程发送
    进程可以通过系统调用 kill() 向其他进程发送信号:
复制
#include <signal.h>
int kill(pid_t pid, int sig);  // 向指定PID的进程发送信号
// 实例
kill(1234, SIGTERM) // 向PID为1234的进程发送终止信号。
kill(-1, SIGKILL) // 向所有进程发送 SIGKILL(需权限)。

kill(1234, SIGTERM) 向PID为1234的进程发送终止信号。

kill(-1, SIGKILL) 向所有进程发送 SIGKILL(需权限)。

  1. 由用户或终端触发
    键盘快捷键:
  • Ctrl+C → 发送 SIGINT(中断进程)。

  • Ctrl+Z → 发送 SIGTSTP(暂停进程)。

  • Ctrl+\ → 发送 SIGQUIT(终止并生成核心转储)。

  • 命令行工具:
    kill -9 PID:发送 SIGKILL(强制终止进程)。
    pkill -HUP process_name:发送 SIGHUP(重新加载配置)。

  1. 进程自身触发
    进程可以通过 raise() 或 kill() 向自己发送信号:
raise(SIGALRM);  // 等同于 kill(getpid(), SIGALRM);

常见用途:

定时器到期(SIGALRM)。

自定义信号处理(如 SIGUSR1/SIGUSR2)。

  1. 通过条件触发
    某些系统调用或条件会隐式产生信号:

alarm():设置定时器,到期后发送 SIGALRM。

sigqueue():发送信号并附加数据(实时信号)。

管道或套接字断开:如写入已关闭的管道会触发 SIGPIPE。

复制
#include <signal.h>
#include <stdio.h>

void handler(int sig) {
    printf("Received SIGINT!\n");
}

int main() {
    signal(SIGINT, handler);  // 注册处理函数
    while(1) {}               // 无限循环
}

四、信号的保存(重要)

1. 操作系统如何管理信号

这里我们首先首先要列出内核的信号相关的数据结构
在这里插入图片描述
在这里插入图片描述
内核中,我们的进程数据结构(struct task_struct)中维护了三张表:

  • block : 阻塞表,表示哪些信号是需要阻塞的
  • pending: 未决的信号,比如我们一个信号被进程接受到,但是block表中阻塞了他,这个时候,我们的pending表中对应的信号的位置就会置一,表示这个信号处于未决的状态。
  • handler表:存储我们对应的函数指针数组,记录没有对应的信号的处理方式,我们的signal函数就是将对应的函数指针填充进入这张表中。

2. 我们如何在用户层修改表结构

这里的各种表结构都是位图(操作系统减少内存的消耗),那么既然使用了位图,操作这些表就会比较困难,所以操作系统为我们提供了相关的接口。
在这里插入图片描述
这里的sigset就是我们的位图结构,我们可以通过这些接口,操作一个创建出一个定制化的位图结构。
在这里插入图片描述
sigpending就是回去当前(调用这个接口的是偶)的pending表。
在这里插入图片描述
sigprocmask可以根据我们的需求定制化设置block表的内容,并且可以取出原来的block表。

3. 当信号来到我们的进程的时候,我们的操作如何对其进行组织和管理

在这里插入图片描述

  1. 当信号来到我们的进程的时候,我们的进程首先进行上下文保护,将我们当前执行的情况保存,然后陷入内核。
  2. 处理本次信号的问题:但是在处理之前,我们需要检查一下block表,1. 如何什么block表表示我们当前的信号是阻塞的,将pending表中对应的位置置一,表示这个信号处于未决的状态,恢复上下文,回到用户态,继续刚才的代码。 2.如何信号没有阻塞,那么回到用户态,执行我们定制化的handler(也可能是退出)。
  3. 再次回到内核态(为什么这次还要回到内核态呢? 当然是因为我们还要找到刚才代码执行的位置)。
  4. 这个时候从内核态找到刚才执行的位置,再次回到用户态继续执行刚才的代码。

细节剖析:

  • 细节一: 操作系统规定,每次从内核态到用户态的时候都要检查pending表。

这里可以用以个场景来解释pending表检查:
我们的代码最开始的时候block将SIGINT信号进行的阻塞,表示不处理这个信号
这个时候如果我们接收到了SIGINT信号,这个时候pending表中SIGINT被置一,表示
未决, 后续我们如果通过sigprocmask修改block表,SIGINT不阻塞了,这个操作并不会
触发信号检查
(也就是说就算我们这个信号不阻塞了,也不会检查pending表是否有未决的对应信号),也就是说,如果我的后续代码没有任何系统调用,那么即使pending[SIGINT] == 1
仍然不会触发任何检查,只有我们在其他地方触发系统调用(getpid() , 或者类似再次SIGINT),才会调用

也就是说任何的系统调用都会触发pending表检查

  • 细节二: 我们总是在内核态转成用户态的时候检查了pending表(是否有未决的信号)

五、信号的处理机制

信号既然能够产生,那么我们肯定可以针对自身的需求,定制对应的回调处理函数,操作系统也为我们提供了对应的函数的接口,这个接口的名字就叫做signal
在这里插入图片描述

  • signum : 就是表示我们想要针对哪个函数定义对应的回调机制。
  • handler: 对应的回调函数,这里的参数是一个函数指针
    有了这种机制,我们就可以定制化我们信号处理机制。
    在这里插入图片描述

在这里插入图片描述
我这里提供了一个实例代码,我通过signal函数屏蔽了CTRL + C的默认功能,可以看见,当我进行ctrl + c的时候,程序并没有退出,而是执行我定义的handler函数。

一些注意事项

  1. 虽然操作系统给我们提供了定制化handler的接口,但是也不能让我们为所欲为,比如前面说的SIGKILL接口就不能重新设置,这保证我们总是可以kill -9杀掉这个进程。
  2. 默认和忽略
#define SIG_DFL ((__sighandler_t)  0))
#define SIG_IGN ((__sighandler_t)  1))

操作系统给我们定义了一些宏帮助我们进程默认和忽略设置,比如如果我将signal的第二个参数设置成SIG_IGN就表示我忽略这个信号

基于信号再谈谈操作系统

看到了信号,信号是如何触发的,无非就是内核产生(软件中断),硬件中断,也就是说信号是基于中断产生的,一旦有中断就产生对应的信号,那操作系统呢,也是一样的,操作系统就是基于软件中断(系统调用),硬件中断进行执行,我们可以这样理解,操作系统就是一个死循环。
在这里插入图片描述
当各种硬件中断来了(按了键盘)就执行对应的硬件中断函数,当触发了软中断(各种系统调用getpid(), open()),就调用软件中断表中的对应函数。
总结:操作系统,就是基于中断的可执行程序。

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

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

相关文章

经典频域分析法(Bode图、Nyquist判据) —— 理论、案例与交互式 GUI 实现

目录 经典频域分析法(Bode图、Nyquist判据) —— 理论、案例与交互式 GUI 实现一、引言二、经典频域分析方法的基本原理2.1 Bode 图分析2.2 Nyquist 判据三、数学建模与公式推导3.1 一阶系统的频域响应3.2 多极系统的 Bode 图绘制3.3 Nyquist 判据的数学描述四、经典频域分析…

使用scoop一键下载jdk和实现版本切换

安装 在 PowerShell 中输入下面内容&#xff0c;保证允许本地脚本的执行&#xff1a; set-executionpolicy remotesigned -scope currentuser然后执行下面的命令安装 Scoop&#xff1a; iwr -useb get.scoop.sh | iex国内用户可以使用镜像源安装&#xff1a;powershell iwr -us…

对状态模式的理解

对状态模式的理解 一、场景二、不采用状态模式1、代码2、缺点 三、采用状态模式1、代码1.1 状态类1.2 上下文&#xff08;这里指&#xff1a;媒体播放器&#xff09;1.3 客户端 2、优点 一、场景 同一个东西&#xff08;例如&#xff1a;媒体播放器&#xff09;&#xff0c;有一…

vue2(webpack)集成electron和 electron 打包

前言 之前发过一篇vue集成electron的文章&#xff0c;但是用vue3vite实现的&#xff0c;在vue2webpack工程可能不适用&#xff0c;所以这篇文章就主要介绍vue2webpack集成electron方法 创建项目 vue create vue-electron-demo目录架构 vue-electron-demo/ ├── src/ …

C++内存管理优化实战:提升应用性能与效率

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家、CSDN平台优质创作者&#xff0c;高级开发工程师&#xff0c;数学专业&#xff0c;拥有高级工程师证书&#xff1b;擅长C/C、C#等开发语言&#xff0c;熟悉Java常用开发技术&#xff0c;能熟练应用常用数据库SQL server,Oracle…

redis数据迁移之通过redis-dump镜像

这里写目录标题 一、redis-dump 镜像打包1.1 安装windows docker1.2 idea项目创建1.3 idea镜像打包 二、redis数据迁移2.1 数据导出2.2 数据导入 一、redis-dump 镜像打包 没有找到可用的redis-dump镜像&#xff0c;需要自己打包一下&#xff0c;这里我是在idea直接打包的 1.…

redis导入成功,缺不显示数据

SpringBootTest class SecurityApplicationTests {AutowiredStringRedisTemplate template; //添加这句代码&#xff0c;自动装载&#xff0c;即可解决文章三处代码报错Testvoid contextLoads() {String compact Jwts.builder().signWith(Jwts.SIG.HS512.key().build()).subj…

从表格到序列:Swift 如何优雅地解 LeetCode 251 展开二维向量

文章目录 摘要描述题解答案题解代码分析示例测试及结果时间复杂度空间复杂度总结 摘要 在这篇文章中&#xff0c;我们将深入探讨 LeetCode 第 251 题——“展开二维向量”的问题。通过 Swift 语言&#xff0c;我们不仅会提供可运行的示例代码&#xff0c;还会结合实际场景进行…

小型园区网实验

划分VLAN SW3 [sw3]vlan batch 2 3 20 30 [sw3]interface GigabitEthernet 0/0/1 [sw3-GigabitEthernet0/0/1]port link-type access [sw3-GigabitEthernet0/0/1]port default vlan 2 [sw3-GigabitEthernet0/0/1]int g0/0/2 [sw3-GigabitEthernet0/0/2]port link-type acces…

c# 数据结构 链表篇 有关单链表的一切

本人能力有限,本文仅作学习交流与参考,如有不足还请斧正 目录 0.单链表好处 0.5.单链表分类 1.无虚拟头节点情况 图示: 代码: 头插/尾插 删除 搜索 遍历全部 测试代码: 全部代码 2.有尾指针情况 尾插 全部代码 3.有虚拟头节点情况 全部代码 4.循环单链表 几个…

VS Code连接服务器编写Python文件

1、下载 Visual Studio Code 2、打开扩展&#xff08;ctrl shift x ) 3、搜索 Remote - SSH&#xff0c;安装 4、F1 或者 点金左下角 5、选择&#xff1a;Remote-SSH: Connect to Host……&#xff0c;回车 6、第一次用的时候&#xff0c;VS Code 会提示添加 SSH 主机。输…

Gitea的安装和配置以及应用

Gitea的安装和配置以及应用 一、安装 1、创建数据库和数据库账户&#xff08;pg&#xff09; su – postgres -c "psql" CREATE ROLE gitea WITH LOGIN PASSWORD gitea; CREATE DATABASE giteadb WITH OWNER gitea TEMPLATE template0 ENCODING UTF8 LC_COLLATE …

$_GET变量

$_GET 是一个超级全局变量&#xff0c;在 PHP 中用于收集通过 URL 查询字符串传递的参数。它是一个关联数组&#xff0c;包含了所有通过 HTTP GET 方法发送到当前脚本的变量。 预定义的 $_GET 变量用于收集来自 method"get" 的表单中的值。 从带有 GET 方法的表单发…

TBE(TVM的扩展)

算子 张量 一个张量只有一种数据类型 在内存中只能线性存储&#xff0c;最终形成一个长的一维数组 晟腾AI的数据格式 AIPP是对我们常见的数据格式转化成AI core支持的数据格式 广播机制 TVM TBE的第一种开发方式&#xff1a;DSL TBE的第二种开发方式&#xff1a;TVM TBE的第…

【Function Calling与Tool Calling】深度解析大模型智能中枢的架构革命

目录 一、范式转移&#xff1a;从对话引擎到智能中枢 二、核心技术解析 2.1 Function Calling技术栈 2.2 Tool Calling实现模式 三、企业级应用架构设计 3.1 智能工单系统案例 3.2 性能优化策略 四、安全与治理框架 4.1 权限控制矩阵 4.2 审计追踪设计 五、开发者实…

知识表示方法之六:过程表示法(Procedural Representation)

在人工智能的发展史中&#xff0c;关于知识的表示方法曾存在两种不同的观点。一种观点认为知识主要是陈述性的&#xff0c;其表示方法应着重将其静态特性&#xff0c;即事物的属性以及事物间的关系表示出来&#xff0c;称以这种观点表示知识的方法为陈述式或说明式表示法&#…

sql-labs靶场 less-2

文章目录 sqli-labs靶场less 2 联合注入 sqli-labs靶场 每道题都从以下模板讲解&#xff0c;并且每个步骤都有图片&#xff0c;清晰明了&#xff0c;便于复盘。 sql注入的基本步骤 注入点注入类型 字符型&#xff1a;判断闭合方式 &#xff08;‘、"、’、“”&#xf…

git clone(复制)下载

1、复制 下载地址 2、打开网页&#xff0c;点击 克隆/下载按扭 3、按提示复制命令行到终端 4、VS里打开终端&#xff0c;并粘贴以下命令 5、 下载完毕 6、复制文件夹到你选定的位置 7、用VSCODE打开文件夹&#xff0c;开始你接下来的工作

Android设置adjustResize时无法生效 解决办法

删除Activity类下执行全屏的一行参数。 将图中这段Activity类中执行命令给删除就解决了。 注意关闭后状态栏和导航栏的透明度就无法自动处理了&#xff0c;需要到values和values-night下的themes.xml手动设置状态栏背景颜色。 <item name"android:statusBarColor"…

按键长按代码

这些代码都存放在定时器中断中。中断为100ms中断一次。 数据判断&#xff0c;看的懂就看吧