【Linux探索学习】第二十八弹——信号(下):信号在内核中的处理及信号捕捉详解

Linux学习笔记:

https://blog.csdn.net/2301_80220607/category_12805278.html?spm=1001.2014.3001.5482

前言:

在前面我们已经学习了有关信号的一些基本的知识点,包括:信号的概念、信号产生和信号处理等,今天我们重点来讲解一下信号在内核中的处理以及信号捕捉的相关知识点

在这篇文章中,我们将深入探讨 Linux 信号在内核中的处理流程,详细讲解信号递达、信号阻塞、未决信号、信号集操作、信号捕捉等内容,并通过大量的代码示例和实际场景来展示信号如何在 Linux 中运作。

与信号有关的还有一个很重要的知识点是有关用户态、内核态和状态切换的知识,本篇没有进行讲解,需要自己再去了解一下

目录

1. 信号在内核中的处理流程

1.1 信号在内核中的表示

1.2 信号的递达机制

信号递达的条件

信号递达过程

示例代码:信号递达

1.3 信号未决状态

信号未决队列的管理

示例代码:查看未决信号

1.4 信号集与 sigset_t

信号集的操作

示例代码:操作信号集

​编辑

1.5 信号屏蔽与 sigprocmask

示例代码:使用 sigprocmask() 阻塞和解除阻塞信号

1.6 获取未决信号:sigpending()

示例代码:使用 sigpending() 查看未决信号

2. 信号捕捉与处理

2.1 使用 signal() 捕捉信号

示例代码:使用 signal() 捕捉信号

2.2 使用 sigaction() 捕捉信号

示例代码:使用 sigaction() 捕捉信号

3. 总结


1. 信号在内核中的处理流程

信号是由内核或其他进程通过系统调用发送给目标进程的。当进程正在执行时,信号能够在不干扰进程当前操作的情况下打断它的执行,触发某种特定的行为。信号的处理流程在 Linux 内核中被设计得非常灵活,既支持异步信号处理,又能通过进程的信号屏蔽机制来控制信号的递达。

1.1 信号在内核中的表示

信号在内核中的表示示意图:

  • 每个信号都有两个标志位分别表示阻塞(block)和未决(pending),还有一个函数指针表示处理动作。信号产生时,内核在进程控制块中设置该信号的未决标志,直到信号递达才清除该标志。在上图的例子中,SIGHUP信号未阻塞也未产生过,当它递达时执行默认处理动作。
  • SIGINT信号产生过,但正在被阻塞,所以暂时不能递达。虽然它的处理动作是忽略,但在没有解除阻塞之前不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除阻塞。
  • SIGOUIT信号未产生过,一旦产生SIGOUIT信号将被阻塞,它的处理动作是用户自定义函数sighandler。如果在进程解除对某信号的阻塞之前这种信号产生过多次,将如何处理?POSIX1允许系统递送该信号一次或多次。Linux是这样实现的:常规信号在递达之前产生多次只计一次,而实时信号在递达之前产生多次可以依次放在一个队列里。本章不讨论实时信号。

1.2 信号的递达机制

信号递达是信号机制中的核心概念,它是信号从信号源发送到目标进程的过程。信号递达的实现依赖于内核的进程调度机制。在进程执行过程中,内核需要判断该进程是否有需要处理的未决信号,信号的递达会在进程的上下文切换时被触发。

信号递达的条件

信号的递达取决于以下几个因素:

  1. 信号是否被屏蔽:每个进程都可以选择性地阻塞某些信号,当信号被阻塞时,它们会进入未决状态,直到信号被解除阻塞。
  2. 进程的当前状态:信号递达的时机还受进程状态的影响。如果进程处于不可中断的状态(例如执行系统调用),它可能无法立即处理信号,这时信号会被推迟递达,直到进程能够响应信号为止。
  3. 信号类型:标准信号和实时信号在递达的优先级上可能存在差异。实时信号(编号从 SIGRTMIN 开始)通常会比标准信号更快地递达,并且能够提供更多的控制选项。
信号递达过程

信号的递达过程通常包括以下几个步骤:

  1. 信号的发送:信号可以通过内核发送(例如内核事件或系统调用)或通过其他进程调用 kill() 函数发送。
  2. 信号的处理检查:当一个进程正在被调度执行时,内核会检查该进程是否有未决的信号。如果存在未决信号,内核会查看进程的信号屏蔽字,以决定这些信号是否可以递达。
  3. 信号的递送:如果信号未被屏蔽且能够递达,内核会根据进程的信号处理方式来决定是执行默认动作还是调用信号处理函数。
示例代码:信号递达
#include <stdio.h>
#include <signal.h>
#include <unistd.h>void signal_handler(int sig) {printf("Received signal: %d\n", sig);
}int main() {signal(SIGINT, signal_handler);  // 捕捉 SIGINT 信号printf("Waiting for SIGINT...\n");while(1) {sleep(1);  // 进入等待状态,直到接收到信号}return 0;
}

在上面的代码中,进程会一直运行并等待 SIGINT 信号(通常由按下 Ctrl+C 触发)。一旦进程接收到 SIGINT 信号,内核会将其递送到进程,并触发信号处理函数 signal_handler

1.3 信号未决状态

当信号发送给进程时,如果该信号被进程的信号屏蔽字阻塞,那么该信号就会进入未决状态。未决信号是那些已经被发送但尚未被递达的信号。内核维护了每个进程的未决信号队列,并会在进程解除对该信号的阻塞时按顺序递送这些信号。

信号未决队列的管理

在 Linux 内核中,每个进程都有一个 task_struct 结构体,其中包含了当前进程的未决信号集合。每当一个信号发送给一个进程时,如果该信号被阻塞,内核不会立即递送它,而是将其存放在进程的未决信号队列中,直到进程解除对该信号的阻塞。

未决信号通常在进程解除信号屏蔽字后,由内核递送。递送顺序通常与信号发送顺序一致,且会按照优先级递送实时信号和标准信号。

示例代码:查看未决信号
#include <stdio.h>
#include <signal.h>
#include <unistd.h>int main() {sigset_t pending;sigpending(&pending);  // 获取当前进程的未决信号if (sigismember(&pending, SIGINT)) {printf("SIGINT is pending.\n");} else {printf("No SIGINT pending.\n");}sleep(10);  // 稍作停顿,方便查看信号状态return 0;
}

通过使用 sigpending() 函数,我们可以查看当前进程的未决信号集。如果进程没有处理 SIGINT 信号,且信号被阻塞,则该信号会处于未决状态。

1.4 信号集与 sigset_t

信号集(sigset_t)是一个用于表示信号集合的数据结构,它通过位掩码的方式表示进程当前可以接受的信号集合。sigset_t 通常是一个整数或更大的数据类型,每一位对应一个信号。

信号集的操作

在 Linux 中,常用的信号集操作函数包括:

  • sigemptyset():初始化信号集为空集。
  • sigaddset():将某个信号添加到信号集中。
  • sigdelset():将某个信号从信号集中删除。
  • sigismember():判断某个信号是否在信号集中。
示例代码:操作信号集
#include <signal.h>
#include <stdio.h>int main() {sigset_t set;sigemptyset(&set);         // 初始化为空集sigaddset(&set, SIGINT);   // 将 SIGINT 添加到信号集中sigaddset(&set, SIGTERM);  // 将 SIGTERM 添加到信号集中if (sigismember(&set, SIGINT)) {printf("SIGINT is in the set.\n");}sigdelset(&set, SIGINT);   // 从信号集中删除 SIGINTif (!sigismember(&set, SIGINT)) {printf("SIGINT is no longer in the set.\n");}return 0;
}

1.5 信号屏蔽与 sigprocmask

sigprocmask() 是一个用于修改进程信号屏蔽字的系统调用,它可以用来阻塞、解除阻塞或查询进程的信号屏蔽字。信号屏蔽字定义了哪些信号是被阻塞的,从而影响信号递达的时机。

sigprocmask() 具有以下操作模式:

  • SIG_BLOCK:将指定的信号添加到信号屏蔽字中,阻塞这些信号。
  • SIG_UNBLOCK:从信号屏蔽字中删除指定信号,解除阻塞。
  • SIG_SETMASK:将信号屏蔽字设置为指定值,替换当前的信号屏蔽字。
示例代码:使用 sigprocmask() 阻塞和解除阻塞信号
#include <signal.h>
#include <stdio.h>
#include<unistd.h>int main() {sigset_t new_mask, old_mask;sigemptyset(&new_mask);sigaddset(&new_mask, SIGINT);  // 阻塞 SIGINTsigprocmask(SIG_BLOCK, &new_mask, &old_mask);  // 阻塞 SIGINT// 信号屏蔽后,可以进行一些操作printf("SIGINT is blocked. Press Ctrl+C to send SIGINT.\n");sleep(10);  // 暂停,等待 Ctrl+C 输入// 恢复信号屏蔽字sigprocmask(SIG_SETMASK, &old_mask, NULL);  // 恢复原信号屏蔽字printf("SIGINT is unblocked.\n");sleep(10);  // 等待信号递达return 0;
}

在这段代码中,SIGINT 信号在前 10 秒内被阻塞,用户按下 Ctrl+C 时信号不会立即递达。10 秒后,信号屏蔽被解除,SIGINT 信号会被递送并触发相应的处理。

1.6 获取未决信号:sigpending()

sigpending() 函数用于获取当前进程的未决信号,它返回一个信号集,表示该进程尚未处理的信号集合。sigpending() 的实现依赖于进程的信号队列,它可以用于调试和监控进程的信号处理状态。

示例代码:使用 sigpending() 查看未决信号
#include <signal.h>
#include <stdio.h>
#include <unistd.h>int main() {sigset_t pending;sigemptyset(&pending);// 阻塞 SIGINT 信号sigset_t mask;sigemptyset(&mask);sigaddset(&mask, SIGINT);sigprocmask(SIG_BLOCK, &mask, NULL);// 发送 SIGINT 信号,但它会被阻塞kill(getpid(), SIGINT);// 获取未决信号sigpending(&pending);if (sigismember(&pending, SIGINT)) {printf("SIGINT is pending.\n");} else {printf("No SIGINT pending.\n");}sleep(5);  // 稍作等待,防止信号丢失return 0;
}

此程序模拟了阻塞 SIGINT 信号并通过 sigpending() 查看进程的未决信号状态。如果信号被阻塞,它将在信号屏蔽字解除后递达。


2. 信号捕捉与处理

信号捕捉是指进程通过自定义信号处理函数来响应特定的信号。Linux 提供了 signal()sigaction() 两种方式来捕捉信号。signal() 是一种简单的接口,而 sigaction() 提供了更为复杂的配置选项,使得开发者能够在处理信号时获得更多的控制权。

2.1 使用 signal() 捕捉信号

signal() 是最基础的信号捕捉方式,它允许开发者指定一个信号处理函数来响应特定信号。signal() 的使用非常简单,但它并不支持所有高级功能,如信号的重入处理或复杂的信号控制。

示例代码:使用 signal() 捕捉信号
#include <signal.h>
#include <stdio.h>
#include<unistd.h>void signal_handler(int sig) {printf("Received signal: %d\n", sig);
}int main() {signal(SIGINT, signal_handler);  // 捕捉 SIGINT 信号printf("Waiting for SIGINT...\n");while (1) {sleep(1);  // 程序将一直运行,直到接收到信号}return 0;
}

在这个示例中,signal_handler 函数会在接收到 SIGINT 信号时被调用。

2.2 使用 sigaction() 捕捉信号

sigaction() 提供了比 signal() 更灵活的方式来处理信号。它允许开发者在捕捉信号时设定更多的参数,比如如何处理重入信号、是否需要恢复默认行为等。

sigaction() 的结构体定义如下:

struct sigaction {void (*sa_handler)(int);sigset_t sa_mask;int sa_flags;void (*sa_restorer)(void);
};
  • sa_handler:指定信号处理函数。
  • sa_mask:指定在信号处理期间需要阻塞的信号集。
  • sa_flags:设定信号处理的行为。
示例代码:使用 sigaction() 捕捉信号
#include <signal.h>
#include <stdio.h>
#include<unistd.h>void signal_handler(int sig) {printf("Received signal: %d\n", sig);
}int main() {struct sigaction sa;sa.sa_handler = signal_handler;  // 指定信号处理函数sa.sa_flags = 0;sigemptyset(&sa.sa_mask);  // 初始化信号集sigaction(SIGINT, &sa, NULL);  // 捕捉 SIGINT 信号printf("Waiting for SIGINT...\n");while (1) {sleep(1);  // 程序将一直运行,直到接收到信号}return 0;
}

通过 sigaction(),程序能够灵活地处理信号,并控制信号捕捉的行为,甚至允许在处理信号时阻塞其他信号。

3. 总结

本文我们讲解了信号的处理机制,并且对信号捕捉进行了更详细的补充,结合上篇内容,基本上将信号部分的内容进行了大概的讲解,认真看一下相信会对你有所帮助

感谢各位大佬观看,创作不易,还望各位大佬点赞支持!!!

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

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

相关文章

Qt中使用QPdfWriter类结合QPainter类绘制并输出PDF文件

一.类的介绍 1.QPdfWriter介绍 Qt中提供了一个直接可以处理PDF的类&#xff0c;这就是QPdfWriter类。 &#xff08;1&#xff09;PDF文件生成 支持创建新的PDF文件或覆盖已有文件&#xff0c;通过构造函数直接绑定文件路径或QFile对象&#xff1b; 默认生成矢量图形PDF&#…

快速上手gdb/cgdb

Linux调试器-gdb使用 1.背景2.调试原理、技巧命令2.1指令2.2 本质2.3 技巧 1.背景 程序的发布方式有两种&#xff0c;debug模式和release模式 Linux gcc/g出来的二进制程序&#xff0c;默认是release模式 要使用gdb调试&#xff0c;必须在源代码生成二进制程序的时候, 加上 -g…

linux网络编程(1.5w字+内部程序理解网络)

目录 核心大图&#xff1a; 网络字节序 网络字节序与主机字节序 地址转换函数 一、inet_ntoa函数 二、inet_aton函数 三、inet_aton和inet_ntoa的测试 in_addr转字符串的函数: socket编程接口 socket 常见API 1.socket 参数1&#xff1a;int af 参数2&#xff1a;…

windows环境下用docker搭建php开发环境dnmp

安装WSL WSL即Linux子系统&#xff0c;比虚拟机占用资源少&#xff0c;安装的前提是系统必须是win10以上。 WSL的安装比较简单&#xff0c;网上有很多教程&#xff0c;例如&#xff1a;WSL简介与安装流程&#xff08;Windows 下的 Linux 子系统&#xff09;_wsl安装-CSDN博客&…

Nginx之rewrite重写功能

目录 一、rewrite概述 1、rewrite功能 2、跳转场景 二、标准配置指令 1、rewrite日志记录指令 2、未初始化变量告警日志记录指令 3、rewrite 指令 3.1 正则表达式 三、rewrite模块使用实例 1.基于域名的跳转 2.基于客户端 IP 访问跳转 3.?基于旧域名跳转到新域名后…

Mybatis(一)

配置文件 必要的用户密码要修改, 还有绿色线的名字要修改成数据库的 配置文件直接cv 创建 复习之前的知识进行分层处理 与前面一一对应, 后面三个发现后面输出是null, 没有一一对应, 后面再解释解决方法 运行发现, 输出正常 idea的测试类 两个注解了解 记得加上这个, 不然无…

一周学会Flask3 Python Web开发-http响应状态码

锋哥原创的Flask3 Python Web开发 Flask3视频教程&#xff1a; 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 在Flask程序中&#xff0c;客户端发出的请求触发相应的视图函数&#xff0c;获取返回值会作为响应的主体&#xff0c;最后生成…

七星棋牌源码高阶技术指南:6端互通、200+子游戏玩法深度剖析与企业级搭建实战(完全开源)

在棋牌游戏行业高速发展的今天&#xff0c;如何构建一个具备高并发、强稳定性与多功能支持的棋牌游戏系统成为众多开发者和运营团队关注的焦点。七星棋牌全开源修复版源码 凭借其 六端互通、200子游戏玩法、多省区本地化支持&#xff0c;以及 乐豆系统、防沉迷、比赛场、AI智能…

C++和OpenGL实现3D游戏编程【总览】

欢迎来到zhooyu的游戏专栏。 主页网址&#xff1a;【zhooyu】 专栏网址&#xff1a;【C和OpenGL实现3D游戏编程】 &#x1f31f;&#x1f31f;&#x1f31f;这里将通过一个OpenGL实现3D游戏编程实例教程&#xff0c;带大家深入学习OpenGL知识。知识无穷而人力有穷&#xff0c;…

pycharm社区版有个window和arm64版本,到底下载哪一个?还有pycharm官网

首先pycharm官网是这一个。我是在2025年2月16日9:57进入的网站。如果网站还没有更新的话&#xff0c;那么就往下滑一下找到 community Edition,这个就是社区版了免费的。PyCharm&#xff1a;适用于数据科学和 Web 开发的 Python IDE 适用于数据科学和 Web 开发的 Python IDE&am…

GPT-Sovits:语音克隆训练-遇坑解决

前言 本来以为3050完全无法执行GPT-Sovits训练的&#xff0c;但经过实践发现其实是可以&#xff0c;并且仅花费了十数分钟便成功训练和推理验证了自己的语音模型。 官方笔记&#xff1a;GPT-SoVITS指南 语雀 项目地址&#xff1a;https://github.com/RVC-Boss/GPT-SoVITS 本人…

8 SpringBootWeb案例(上): 查询【分页功能(分页插件)】、删除、新增、修改

文章目录 前言:SpringBootWeb案例1. 准备工作1.1 需求&环境搭建1.1.1 需求说明1.1.2 环境搭建1.2 开发规范1.2.1 开发规范-REST(不强求非要这种风格,传统风格有时候更方便)1.2.2 开发规范-统一响应结果和异常处理1.2.3 开发流程2. 部门管理2.1 查询部门2.1.1 原型和需求…

深入了解 DevOps 基础架构:可追溯性的关键作用

在当今竞争激烈的软件环境中&#xff0c;快速交付强大的应用程序至关重要。尽管如此&#xff0c;在不影响质量的情况下保持速度可能是一项艰巨的任务&#xff0c;这就是 DevOps 中的可追溯性发挥作用的地方。通过提供软件开发生命周期 &#xff08;SDLC&#xff09; 的透明视图…

用C++ Qt实现安卓电池充电动效 | 打造工业级电量控件

一、为什么需要自定义电池控件&#xff1f; 在工业控制、车机系统、智能硬件等领域的UI开发中&#xff0c;电池状态显示是高频出现的UI组件。通过实现一个支持颜色渐变、动态充电动画、警戒阈值提示的电池控件&#xff0c;开发者可以系统掌握以下核心能力&#xff1a; Qt绘图…

一批起飞猪名单配图

好久没有使用风口猪选股指标了&#xff0c;今天去玩了一把&#xff0c;发现起飞猪指标显示了好多一批猪票 华曙高科 汉威科技 双林股份 曼恩斯特 长盈精密 江苏雷利 双飞集团 奥飞数据 硅宝科技 水晶光电 长盈精密

机器学习笔记——常用损失函数

大家好&#xff0c;这里是好评笔记&#xff0c;公主号&#xff1a;Goodnote&#xff0c;专栏文章私信限时Free。本笔记介绍机器学习中常见的损失函数和代价函数&#xff0c;各函数的使用场景。 热门专栏 机器学习 机器学习笔记合集 深度学习 深度学习笔记合集 文章目录 热门…

Ubuntu 服务器Llama Factory 搭建DeepSeek-R1微调训练环境

1.首先了解一下什么是LLM微调 LLM 微调指的是在已经预训练好的大型语言模型基础上&#xff0c;使用特定的任务数据或领域数据&#xff0c;通过进一步的训练来调整模型的参数&#xff0c;使其在特定任务或领域上能够表现得更好。简单来说&#xff0c;就是对一个已经具备了丰富语…

环境变量与本地变量

目录 本地变量的创建 环境变量VS本地变量 认识完了环境变量我们来认识一下本地变量。 本地变量的创建 我们如果直接env是看不到本地变量的&#xff0c;因为本地变量和环境变量都具有独立性&#xff0c;环境变量是系统提供的具有全局属性的变量&#xff0c;都存在bash进程的…

智慧农业新生态 | 农业数字化服务平台——让土地生金,让服务无忧

一部手机管农事&#xff0c;从播种到丰收&#xff0c;全链路数字化赋能&#xff01; 面向农户、农机手、农服商、农资商打造的一站式农业产业互联网平台&#xff0c;打通农资交易、农机调度、农服管理、技术指导全场景闭环&#xff0c;助力乡村振兴提效增收。 三大核心场景&am…

【运维自动化-作业平台】如何创建执行方案和作业模板

蓝鲸智云作业平台&#xff0c;以下简称作业平台或JOB平台作业模板和执行方案&#xff1a;将运维操作场景中涉及到的多个脚本执行或文件分发步骤组合成一个作业模板&#xff0c;这个作业模板尽可能把场景相关的共性逻辑都包含进去&#xff0c;然后再根据实际使用场景衍生出相应的…