当前位置: 首页 > article >正文

【Linux】25.进程信号(2)

文章目录

  • 4.捕捉信号
    • 4.1 重谈地址空间
    • 4.2 内核如何实现信号的捕捉
    • 4.3 sigaction
    • 4.4 可重入函数
    • 4.5 volatile
    • 4.6 SIGCHLD信号(了解)


4.捕捉信号

4.1 重谈地址空间

fdeb1e02c9a416380118e84ff616ef0a

  1. 用户页表有几份?

    有几个进程,就有几份用户级页表–进程具有独立性

  2. 内核页表有几份?

    1份

  3. 每一个进程看到的3~4GB的东西都是一样的。整个系统中进程再怎么切换,3,4GB的空间的内容是不变的。

  4. 进程视角:我们调用系统中的方法,就是在我自己的地址空间中进行执行的。

  5. 操作系统视角:任何一个时刻,都有有进程执行。我们想执行操作系统的代码,就可以随时执行。

  6. 操作系统的本质:基于时钟中断的一个死循环。

  7. 计算机硬件中,有一个时钟芯片,每个很短的时间,向计算机发送时钟中断


4.2 内核如何实现信号的捕捉

如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。

由于信号处理函数的代码是在用户空间的,处理过程比较复杂,举例如下: 用户程序注册了SIGQUIT信号的处理函数sighandler。 当前正在执行main函数,这时发生中断或异常切换到内核态。 在中断处理完毕后要返回用户态的main函数之前检查到有信号SIGQUIT递达。 内核决定返回用户态后不是恢复main函数的上下文继续执行,而是执行sighandler函 数,sighandlermain函数使用不同的堆栈空间,它们之间不存在调用和被调用的关系,是 两个独立的控制流程。 sighandler函数返回后自动执行特殊的系统调用sigreturn再次进入内核态。 如果没有新的信号要递达,这次再返回用户态就是恢复main函数的上下文继续执行了。

92c6c6fb5d1b186954273d41583c30a1


4.3 sigaction

#include <signal.h>
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact); 
  • sigaction函数可以读取和修改与指定信号相关联的处理动作。调用成功则返回0,出错则返回- 1signo是指定信号的编号。若act指针非空,则根据act修改该信号的处理动作。若oact指针非空,则通过oact传出该信号原来的处理动作。actoact指向sigaction结构体。

  • sa_handler赋值为常数SIG_IGN,传给sigaction表示忽略信号。赋值为常数SIG_DFL,表示执行系统默认动作。赋值为一个函数指针表示用自定义函数捕捉信号,或者说向内核注册了一个信号处理函数,该函数返回值为void,可以带一个int参数,通过参数可以得知当前信号的编号,这样就可以用同一个函数处理多种信号。显然,这也是一个回调函数,不是被main函数调用,而是被系统所调用。

sigaction()函数中的两个指针参数 *act*oldact 有不同的用途:

  1. *act (新动作):

    • 指向要设置的新的信号处理方式

    • 如果不为NULL,系统会按照这个结构体设置新的信号处理方式

    • 用于指定我们"想要"的信号处理方式

  2. *oldact (旧动作):

    • 用于保存信号的原有处理方式

    • 如果不为NULL,系统会将原来的信号处理方式保存在这个结构体中

    • 常用于之后恢复原有的信号处理方式

示例:

struct sigaction new_action, old_action;// 设置新的处理方式
new_action.sa_handler = my_handler;    // 设置处理函数
sigemptyset(&new_action.sa_mask);      // 清空信号掩码
new_action.sa_flags = 0;               // 设置标志// 设置SIGINT的处理方式,同时保存原有设置
sigaction(SIGINT, &new_action, &old_action);// ... 一段时间后 ...// 恢复原有的处理方式
sigaction(SIGINT, &old_action, NULL);

注意:

  • 如果只想设置新的处理方式,可以将oldact设为NULL
  • 如果只想查询当前的处理方式,可以将act设为NULLoldact指向一个结构体

当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么它会被阻塞到当前处理结束为止。

如果在调用信号处理函数时,除了当前信号被自动屏蔽之外,还希望自动屏蔽另外一些信号,则用sa_mask字段说明这些需要额外屏蔽的信号,信号处理函数返回时,这些额外屏蔽的信号也会自动解除屏蔽。

代码:

#include <iostream>
#include <cstring>
#include <ctime>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>using namespace std;// 信号处理函数(被注释掉的版本)
// 不是必须调用wait,但建议调用以避免僵尸进程
// void handler(int signo)
// {
//     sleep(5);  // 模拟信号处理需要一定时间
//     pid_t rid;
//     // WNOHANG: 非阻塞等待,如果没有子进程退出立即返回0
//     while ((rid = waitpid(-1, nullptr, WNOHANG)) > 0)
//     {
//         cout << "I am proccess: " << getpid() << " catch a signo: " << signo 
//              << "child process quit: " << rid << endl;
//     }
// }int main()
{// 忽略SIGCHLD信号(17),避免产生僵尸进程// SIG_DFL是默认处理方式,SIG_IGN是忽略信号signal(17, SIG_IGN); // 创建10个子进程for (int i = 0; i < 10; i++){pid_t id = fork();if (id == 0)  // 子进程{while (true){cout << "I am child process: " << getpid() << ", ppid: " << getppid() << endl;sleep(5);break;}cout << "child quit!!!" << endl;exit(0);  // 子进程退出}sleep(1);  // 父进程每隔1秒创建一个子进程}// 父进程循环while (true){cout << "I am father process: " << getpid() << endl;sleep(1);}return 0;
}// 以下是被注释的其他示例代码:// volatile关键字示例
// volatile int flag = 0;  // volatile防止编译器优化// void handler(int signo)
// {
//     cout << "catch a signal: " << signo << endl;
//     flag = 1;
// }// int main()
// {
//     signal(2, handler);  // 设置SIGINT(Ctrl+C)的处理函数
//     // flag可能被优化到CPU寄存器中,volatile防止这种优化
//     while(!flag);  // flag为0时循环继续//     cout << "process quit normal" << endl;
//     return 0;
// }// 信号pending示例
// pending位图从1变为0的时机:在执行信号处理函数之前清零
// 处理信号时会将该信号添加到block表中,防止信号处理函数被重入// 打印当前进程的pending信号集
// void PrintPending()
// {
//     sigset_t set;
//     sigpending(&set);  // 获取当前pending的信号集//     // 打印1-31号信号的pending状态
//     for (int signo = 1; signo <= 31; signo++)
//     {
//         if (sigismember(&set, signo))
//             cout << "1";
//         else
//             cout << "0";
//     }
//     cout << "\n";
// }// void handler(int signo)
// {
//     cout << "catch a signal, signal number : " << signo << endl;
//     while (true)
//     {
//         PrintPending();
//         sleep(1);
//     }
// }// sigaction使用示例
// int main()
// {
//     // struct sigaction act, oact;
//     // memset(&act, 0, sizeof(act));
//     // memset(&oact, 0, sizeof(oact));//     // sigemptyset(&act.sa_mask);  // 清空信号屏蔽字
//     // sigaddset(&act.sa_mask, 1); // 添加要屏蔽的信号
//     // sigaddset(&act.sa_mask, 3);
//     // sigaddset(&act.sa_mask, 4);
//     // act.sa_handler = handler;    // 设置信号处理函数
//     // sigaction(2, &act, &oact);   // 设置SIGINT的处理方式//     // while (true)
//     // {
//     //     cout << "I am a process: " << getpid() << endl;
//     //     sleep(1);
//     // }//     return 0;
// }

这段代码主要演示了:

  1. 信号处理和僵尸进程避免
  2. 进程创建和父子进程通信
  3. volatile关键字的使用
  4. 信号的pending机制
  5. sigaction的使用方法

4.4 可重入函数

2e6f9bb11fd6b3bc5cf95107300f3e9b

  • main函数调用insert函数向一个链表head中插入节点node1,插入操作分为两步,刚做完第一步的 时候,因为硬件中断使进程切换到内核,再次回用户态之前检查到有信号待处理,于是切换到sighandler函数,sighandler也调用insert函数向同一个链表head中插入节点node2,插入操作的 两步都做完之后从sighandler返回内核态,再次回到用户态就从main函数调用的insert函数中继续 往下执行,先前做第一步之后被打断,现在继续做完第二步。结果是,main函数和sighandler先后 向链表中插入两个节点,而最后只有一个节点真正插入链表中了。

  • 像上例这样,insert函数被不同的控制流程调用,有可能在第一次调用还没返回时就再次进入该函数,这称为重入,insert函数访问一个全局链表,有可能因为重入而造成错乱,像这样的函数称为 不可重入函数,反之,如果一个函数只访问自己的局部变量或参数,则称为可重入函数。想一下,为什么两个不同的控制流程调用同一个函数,访问它的同一个局部变量或参数就不会造成错乱?

如果一个函数符合以下条件之一则是不可重入的:

  • 调用了mallocfree,因为malloc也是用全局链表来管理堆的。

  • 调用了标准I/O库函数。标准I/O库的很多实现都以不可重入的方式使用全局数据结构。

不可重入函数特征:

  1. 使用静态或全局变量
  2. 返回指向静态变量的指针
  3. 调用malloc/free
  4. 调用不可重入的系统函数

可重入函数特征:

  1. 仅使用局部变量
  2. 数据通过参数传递
  3. 不调用不可重入函数
  4. 不依赖共享资源

4.5 volatile

  1. 基本作用:
// 没有volatile的问题
int flag = 0;
while (!flag) {// 编译器可能优化为死循环// 因为编译器认为没人修改flag
}// 使用volatile解决
volatile int flag = 0;
while (!flag) {// 每次都会从内存重新读取flag// 而不是使用寄存器中的值
}

volatile 作用:保持内存的可见性,告知编译器,被该关键字修饰的变量,不允许被优化,对该变量的任何操作,都必须在真实的内存中进行操作

  1. 常见场景:
// 1. 信号处理
volatile sig_atomic_t signal_flag = 0;void signal_handler(int signo) {signal_flag = 1;  // 修改共享变量
}int main() {signal(SIGINT, signal_handler);while (!signal_flag) {// 等待信号处理程序修改flag}
}// 2. 硬件寄存器访问
volatile uint32_t* hardware_reg = (uint32_t*)0x20000000;
*hardware_reg = 0x1;  // 每次都直接写入硬件
  1. volatile的特性:
volatile int counter = 0;void example() {// 1. 防止优化删除counter++;  // 不会被优化掉// 2. 保证读写顺序int temp = counter;  // 确保在counter++之后读取// 3. 每次都访问内存for (int i = 0; i < 10; i++) {counter++;  // 每次都读写内存}
}
  1. volatile的局限:
// volatile不能保证原子性
volatile int shared = 0;// 多线程访问时仍需要互斥锁
mutex mtx;
void thread_func() {lock_guard<mutex> lock(mtx);shared++;
}

主要作用:

  1. 防止编译器优化
  2. 保证每次都从内存读取
  3. 保证代码执行顺序
  4. 用于多线程共享或硬件访问

不能做到:

  1. 不保证原子性
  2. 不保证线程安全
  3. 不是线程同步工具

4.6 SIGCHLD信号(了解)

之前讲过用waitwaitpid函数清理僵尸进程,父进程可以阻塞等待子进程结束,也可以非阻塞地查询是否有子进程结束等待清理(也就是轮询的方式)。采用第一种方式,父进程阻塞了就不能处理自己的工作了;采用第二种方式,父进程在处理自己的工作的同时还要记得时不时地轮询一下,程序实现复杂。

其实,子进程在终止时会给父进程发SIGCHLD信号,该信号的默认处理动作是忽略。父进程可以自定义SIGCHLD信号的处理函数,这样父进程只需专心处理自己的工作,不必关心子进程了,子进程终止时会通知父进程,父进程在信号处理函数中调用wait清理子进程即可。

相关文章:

【Linux】25.进程信号(2)

文章目录 4.捕捉信号4.1 重谈地址空间4.2 内核如何实现信号的捕捉4.3 sigaction4.4 可重入函数4.5 volatile4.6 SIGCHLD信号&#xff08;了解&#xff09; 4.捕捉信号 4.1 重谈地址空间 用户页表有几份&#xff1f; 有几个进程&#xff0c;就有几份用户级页表–进程具有独立性…...

洛谷 P1387 最大正方形 C语言

题目描述 在一个 n m 的只包含 0 和 1 的矩阵里找出一个不包含 0 的最大正方形&#xff0c;输出边长。 输入格式 输入文件第一行为两个整数 n, m (1 ≤ n, m ≤ 100)&#xff0c;接下来 n 行&#xff0c;每行 m 个数字&#xff0c;用空格隔开&#xff0c;0 或 1。 输出格式 …...

使用React和Material-UI构建TODO应用的前端UI

使用React和Material-UI构建TODO应用的前端UI 引言环境准备代码解析1. 导入必要的模块2. 创建React组件3. 定义函数3.1 获取TODO列表3.2 创建TODO项3.3 更新TODO项3.4 删除TODO项3.5 处理编辑点击事件3.6 关闭编辑对话框3.7 保存编辑内容 4. 使用Effect钩子5. 渲染组件 功能实现…...

2502,索界面3

原文 SonicUI,你从未见过的方便GUI引擎-源码 介绍 SonicUI是基于原生GDIAPI的GUI引擎.它提供了几个简单的UI组件来实现高效的UI效果,如自绘按钮,不规则窗口,动画,窗口中的网径和图像操作方法. 主要目的是用最少的代码来达到最佳效果. 背景 周知,UI开发一般重复用无趣.因此…...

ChatGPT提问技巧:行业热门应用提示词案例--咨询法律知识

ChatGPT除了可以协助办公&#xff0c;写作文案和生成短视频脚本外&#xff0c;和还可以做为一个法律工具&#xff0c;当用户面临一些法律知识盲点时&#xff0c;可以向ChatGPT咨询获得解答。赋予ChatGPT专家的身份&#xff0c;用户能够得到较为满意的解答。 1.咨询法律知识 举…...

[吾爱出品]CursorWorkshop V6.33 专业鼠标光标制作工具-简体中文汉化绿色版

CursorWorkshop V6.33 专业鼠标光标制作工具 链接&#xff1a;https://pan.xunlei.com/s/VOIFeq5DFB9FS56Al_mT2EfdA1?pwd7ij4# 产品概述 Axialis CursorWorkshop 是一个专业光标创作工具它在 Windows 下运行&#xff0c;让您轻松创建高质量的静态和动态光标适用于 Windows …...

《运维:技术的基石,服务的保障》

1. LVS&#xff08;Linux Virtual Server&#xff09;&#xff1a;基于Linux内核的四层负载均衡解决方案 2. Bonding&#xff08;链路聚合&#xff09;&#xff1a;物理网卡冗余与带宽叠加技术 3. RHEL&#xff08;Red Hat Enterprise Linux&#xff09;&#xff1a;企业级Li…...

【C语言】自定义类型讲解

文章目录 一、前言二、结构体2.1 概念2.2 定义2.2.1 通常情况下的定义2.2.2 匿名结构体 2.3 结构体的自引用和嵌套2.4 结构体变量的定义与初始化2.5 结构体的内存对齐2.6 结构体传参2.7 结构体实现位段 三、枚举3.1 概念3.2 定义3.3 枚举的优点3.3.1 提高代码的可读性3.3.2 防止…...

Day25 洛谷 提高- 1007

零基础洛谷刷题记录 Day01 2024.11.18 Day02 2024.11.25 Day03 2024.11.26 Day04 2024.11.28 Day05 2024.11.29 Day06 2024 12.02 Day07 2024.12.03 Day08 2024 12 05 Day09 2024.12.07 Day10 2024.12.09 Day11 2024.12.10 Day12 2024.12.12 Day13 2024.12.16 Day14 2024.12.1…...

LabVIEW涡轮诊断系统

一、项目背景与行业痛点 涡轮机械是发电厂、航空发动机、石油化工等领域的核心动力设备&#xff0c;其运行状态直接关系到生产安全与经济效益。据统计&#xff0c;涡轮故障导致的非计划停机可造成每小时数十万元的经济损失&#xff0c;且突发故障可能引发严重安全事故。传统人…...

GGML、GGUF、GPTQ 都是啥?

GGML、GGUF和GPTQ是三种与大型语言模型(LLM)量化和优化相关的技术和格式。它们各自有不同的特点和应用场景,下面将详细解释: 1. GGML(GPT-Generated Model Language) 定义:GGML是一种专为机器学习设计的张量库,由Georgi Gerganov创建。它最初的目标是通过单一文件格式…...

Kafka 使用说明(kafka官方文档中文)

文章来源:kafka -- 南京筱麦软件有限公司 第 1 步:获取 KAFKA 下载最新的 Kafka 版本并提取它: $ tar -xzf kafka_{{scalaVersion}}-{{fullDotVersion}}.tgz $ cd kafka_{{scalaVersion}}-{{fullDotVersion}} 第 2 步:启动 KAFKA 环境 注意:您的本地环境必须安装 Java 8+。…...

Kubernetes 中 BGP 与二层网络的较量:究竟孰轻孰重?

如果你曾搭建过Kubernetes集群&#xff0c;就会知道网络配置是一个很容易让人深陷其中的领域。在负载均衡器、服务通告和IP管理之间&#xff0c;你要同时应对许多变动的因素。对于许多配置而言&#xff0c;使用二层&#xff08;L2&#xff09;网络就完全能满足需求。但边界网关…...

大模型综述一镜到底(全文八万字) ——《Large Language Models: A Survey》

论文链接&#xff1a;https://arxiv.org/abs/2402.06196 摘要&#xff1a;自2022年11月ChatGPT发布以来&#xff0c;大语言模型&#xff08;LLMs&#xff09;因其在广泛的自然语言任务上的强大性能而备受关注。正如缩放定律所预测的那样&#xff0c;大语言模型通过在大量文本数…...

物理群晖SA6400核显直通win10虚拟机(VMM)

写在前面&#xff1a;请先确保你的核显驱动支持开启SR-IOV 确保你的BIOS开启了以下选项&#xff1a; VT-D VMX IOMMU Above 4G ResizeBAR 自行通过以下命令确认支持情况&#xff1a; dmesg | grep -i iommudmesg | grep DMAR分配1个虚拟vGPU&#xff1a;echo 1 | sudo tee /sy…...

【python】tkinter实现音乐播放器(源码+音频文件)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;专__注&#x1f448;&#xff1a;专注主流机器人、人工智能等相关领域的开发、测试技术。 【python】tkinter实现音乐播放器&#xff08;源码…...

ESP32开发工具介绍:Thonny——初学者的MicroPython利器

文章目录 引言什么是 Thonny?为什么选择 Thonny 开发 ESP32?1. **MicroPython 的天然支持**2. **极简的配置流程**3. **适合快速原型开发**如何用 Thonny 开发 ESP32?步骤 1:准备工作步骤 2:烧录 MicroPython 固件步骤 3:在 Thonny 中连接 ESP32步骤 4:编写并运行代码Th…...

进程的环境变量

export MUDUO_LOG_DEBUG1 ./testif (::getenv("MUDUO_LOG_TRACE"))return true;有时在程序运行前&#xff0c;我们希望设置环境变量。此处::表示全局命名空间。 在类 Unix 系统&#xff08;如 Linux、macOS&#xff09;中&#xff0c;环境变量并不直接存储在堆、栈或…...

Qwen2.5-Max:AI技术的新里程碑

随着人工智能&#xff08;AI&#xff09;技术的不断进步&#xff0c;全球各大科技公司都在竞相推出更强大的语言模型。近日&#xff0c;阿里巴巴发布了其最新的超大规模混合专家模型&#xff08;MoE&#xff09;——Qwen2.5-Max&#xff0c;这一成果不仅在多个基准测试中超越了…...

MyBatis-Plus速成指南:常用注解

Table Name: 概述&#xff1a; MyBatis-Plus 在确定操作的表时&#xff0c;由 BaseMapper的泛型决定&#xff0c;即实体类决定&#xff0c;且默认操作的表名和实体类的类名一致 问题&#xff1a; 如果实体类类型的类名和要操作表的表名不一致会出现什么问题&#xff1f;(把 us…...

Vue.js组件开发-Vue实现上传word模版打印设置自定义样式和布局

要使用 Vue 实现上传 Word 模板、打印并设置自定义样式和布局&#xff0c;可以借助一些工具和库来完成这个任务。 实现步骤 创建 Vue 项目&#xff1a;使用 Vue CLI 创建一个新的 Vue 项目。安装依赖&#xff1a;安装 docx-templates 库来处理 Word 模板&#xff0c;file-sav…...

Linux 压缩打包

Linux压缩打包 文章目录 Linux压缩打包压缩的意义和原理压缩的意义压缩的原理压缩与解压缩的好处 压缩打包命令.zipzip 命令用法unzip 的用法 .gzgzip 的用法gunzip 的用法 .bz2bzip2 的用法bunzip2 的用法 .xzxz 命令用法 tar 04-Linux压缩打包课后习题 压缩的意义和原理 压缩…...

RabbitMQ深度探索:前置知识

消息中间件&#xff1a; 消息中间件基于队列模式实现异步 / 同步传输数据作用&#xff1a;可以实现支撑高并发、异步解耦、流量削峰、降低耦合 传统的 HTTP 请求存在的缺点&#xff1a; HTTP 请求基于响应的模型&#xff0c;在高并发的情况下&#xff0c;客户端发送大量的请求…...

29.日常算法

1. 验证回文串 II 题目来源 给定一个非空字符串 s&#xff0c;请判断如果 最多 从字符串中删除一个字符能否得到一个回文字符串。 示例 1&#xff1a; 输入: s “aba” 输出: true 示例 2&#xff1a; 输入: s “abca” 输出: true 解释: 可以删除 “c” 字符 或者 “b” …...

智慧校园平台:构建现代化教育体系的技术支撑

在当今信息技术飞速发展的时代&#xff0c;智慧校园平台成为了现代教育领域中的重要组成部分。智慧校园平台不仅能够提升学校的管理水平&#xff0c;还能提供更为个性化和高效的教学服务&#xff0c;从而促进学生的全面发展。 数据分析是智慧校园平台的重要组成部分。通过对学生…...

【单层神经网络】基于MXNet的线性回归实现(底层实现)

写在前面 刚开始先从普通的寻优算法开始&#xff0c;熟悉一下学习训练过程下面将使用梯度下降法寻优&#xff0c;但这大概只能是局部最优&#xff0c;它并不是一个十分优秀的寻优算法 整体流程 生成训练数据集&#xff08;实际工程中&#xff0c;需要从实际对象身上采集数据…...

20250204将Ubuntu22.04的默认Dash的shell脚本更换为bash

20250204将Ubuntu22.04的默认Dash的shell脚本更换为bash 2025/2/4 23:45 百度&#xff1a;dash bash https://blog.csdn.net/2201_75772333/article/details/136955776 【Linux基础】dash和bash简介 Dash&#xff08;Debian Almquist Shell&#xff09;和 Bash&#xff08;Bou…...

Golang 并发机制-3:通道(channels)机制详解

并发编程是一种创建性能优化且响应迅速的软件的强大方法。Golang&#xff08;也称为 Go&#xff09;通过通道&#xff08;channels&#xff09;这一特性&#xff0c;能够可靠且优雅地实现并发通信。本文将揭示通道的概念&#xff0c;解释其在并发编程中的作用&#xff0c;并提供…...

Python处理数据库:MySQL与SQLite详解

Python处理数据库&#xff1a;MySQL与SQLite详解 在数据处理和存储方面&#xff0c;数据库扮演着至关重要的角色。Python提供了多种与数据库交互的方式&#xff0c;其中pymysql库用于连接和操作MySQL数据库&#xff0c;而SQLite则是一种轻量级的嵌入式数据库&#xff0c;Pytho…...

可视化大屏在石油方面的应用。

可视化大屏通过整合石油工业全链条数据&#xff0c;构建数字孪生驱动的运营监控体系&#xff0c;显著提升油气勘探、开采、储运及炼化的管理效能。其技术架构依托工业物联网&#xff08;IIoT&#xff09;实时采集钻井参数、管道压力、储罐液位等数据&#xff0c;通过OPC UA协议…...