【Linux】信号--信号的捕捉/可重入函数/volatile/SIGCHLD信号

文章目录

  • 一、信号的捕捉
    • 1.用户态和内核态
    • 2.内核如何实现信号的捕捉
    • 3.sigaction
  • 二、可重入函数
  • 三、volatile
  • 四、SIGCHLD信号

一、信号的捕捉

1.用户态和内核态

用户态的的时候,进行以下操作:1.操作系统自身的资源(getpid,waitpid…)2.硬件资源(printf, write,read)

用户为了访问内核或者硬件资源,必须通过系统调用完成访问。实际执行系统调用”人是“进程”,但是身份其实是内核。往往系统调用比较费时间一些,所以尽量避免频繁调用系统调用

CPU中有两类寄存器:1.可见寄存器2.不可见寄存器。凡是和当前进程强相关的,上下文数据都保存在寄存器中。CR3寄存器表征当前进程的运行级别;0:内核态,3表示用户态

我一直不太理解:我是一个进程,怎么跑到OS中执行方法呢?

每个进程都有自己独立的用户级页表,内核级页表只有一份就够了

每一个进程都有自己的地址空间(用户空间独占)内核空间(被映射到了每一个进程的34G)。进程要访问OS的接口,其实]只需要在自己的地址空间上进行跳转就可以了!!每一个进程都有34GB,都会共享一个内核级页表,无论进程如何切换,会不会更改任何的[3.4]。用户,凭什么能够执行访问内核的接口或者数据呢?系统调用接口,起始的位置会帮你做的!Int 80 --陷入内核

2.内核如何实现信号的捕捉

信号产生的时候,不会被立即处理,而是在合适的时候。从内核态返回用户态的时候,进行处理,说明曾经我一定是先进入了内核态!----系统调用,进程切换

在这里插入图片描述

线程通过系统调用陷入内核,完成了从用户态到内核态的转变你,然后遍历block和pending表,以及映射的hander,对信号进行默认/忽略/自定义的捕捉,对于自定义捕捉,操作系统通过特定的调用,将自己的身份重新改为用户态,执行自定义函数,执行完毕之后,又通过特殊的系统调用sigreturn再次回到内核,继续进行信号检测,然后返回用户模式,从上次被中断的地方继续向下执行。

3.sigaction

#include <signal.h>
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);

sigaction函数可以读取和修改与指定信号相关联的处理动作。调用成功则返回0,出错则返回- 1。signo

是指定信号的编号。若act指针非空,则根据act修改该信号的处理动作。若oact指针非 空,则通过oact传

出该信号原来的处理动作。act和oact指向sigaction结构体:

将sa_handler赋值为常数SIG_IGN传给sigaction表示忽略信号,赋值为常数SIG_DFL表示执行系统默认动

作,赋值为一个函数指针表示用自定义函数捕捉信号,或者说向内核注册了一个信号处理函 数,该函数返回

值为void,可以带一个int参数,通过参数可以得知当前信号的编号,这样就可以用同一个函数处理多种信

号。显然,这也是一个回调函数,不是被main函数调用,而是被系统所调用。

#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <signal.h>

void Count(int cnt)
{
    while(cnt)
    {
        printf("cnt: %2d\r", cnt);
        fflush(stdout);
        cnt--;
        sleep(1);
    }
    printf("\n");
}

void handler(int signo)
{
    std::cout << "get a signo: " << signo << "正在处理中..." << std::endl;
    Count(20);
}

int main()
{
    struct sigaction act, oact;
    act.sa_handler = handler;
    act.sa_flags = 0;
    // 当我们正在处理某一种信号的时候,我们也想顺便屏蔽其他信号,就可以添加到这个sa_mask中
    sigemptyset(&act.sa_mask);
    // sigaddset(&act.sa_mask, 3);
    sigaction(SIGINT, &act, &oact);

    while (true)
        sleep(1);
    return 0;
}

在这里插入图片描述

当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么 它会被阻塞到当前处理结束为止。 如果在调用信号处理函数时,除了当前信号被自动屏蔽之外,还希望自动屏蔽另外一些信号,则用sa_mask字段说明这些需要额外屏蔽的信号,当信号处理函数返回时自动恢复原来的信号屏蔽字。 sa_flags字段包含一些选项,本章的代码都把sa_flags设为0,sa_sigaction是实时信号的处理函数

二、可重入函数

在这里插入图片描述

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

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

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

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

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

三、volatile

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

#include <stdio.h>
#include <unistd.h>
#include <signal.h>

volatile int quit = 0;

void handler(int signo)
{
    printf("%d 号信号,正在被捕捉!\n",signo);
    printf("quit: %d", quit);
    quit = 1;
    printf("->%d\n", quit);
}

int main()
{
    signal(2, handler);
    while (!quit);
    printf("注意,我是正常退出的\n");
    return 0;
}

不加volatile就会一直休眠

在这里插入图片描述

加了之后,收到2号信号后直接退出

在这里插入图片描述

四、SIGCHLD信号

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

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

请编写一个程序完成以下功能:父进程fork出子进程,子进程调用exit(2)终止,父进程自定 义SIGCHLD信号的处理函数,在其中调用wait获得子进程的退出状态并打印。

#include <iostream>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>

void hanlder(int signo)
{
    pid_t id;
    int status;
    if (id = waitpid(-1, &status, 0) > 0)
    {
        std::cout << "wait child process success,lastcode: " << ((status >> 8) & 0xff) << std::endl;
    }
    std::cout << "child is quit" << std::endl;
}
int main()
{
    signal(SIGCHLD, hanlder);
    pid_t id = fork();
    if (id == 0)
    {
        std::cout << "child create seccuess" << std::endl;
        sleep(3);
        exit(2);
    }
    while (true)
    {
        std::cout << "father process is doing other things" << std::endl;
        sleep(1);
    }
    return 0;
}

在这里插入图片描述

事实上,由于UNIX 的历史原因,要想不产生僵尸进程还有另外一种办法:父进程调 用sigaction将SIGCHLD的处理动作置为SIG_IGN,这样fork出来的子进程在终止时会自动清理掉,不 会产生僵尸进程,也不会通知父进程。系统默认的忽略动作和用户用sigaction函数自定义的忽略 通常是没有区别的,但这是一个特例。此方法对于Linux可用,但不保证在其它UNIX系统上都可 用。

请编写程序验证这样做不会产生僵尸进程

#include <iostream>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>

int main()
{
    signal(SIGCHLD, SIG_IGN);
    pid_t id = fork();
    if (id == 0)
    {
        std::cout << "child create success" << std::endl;
        sleep(3);
        exit(2);
    }

    int status = 0;
    id = waitpid(id, &status, 0);
    if (id > 0)
    {
        std::cout << "wait child process success,lastcode: " << ((status >> 8) & 0xff) << std::endl;
    }

    std::cout << "child is quit" << std::endl;
    return 0;
}

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

YOLOv5改进 | 2023 | CARAFE提高精度的上采样方法(助力细节长点)

一、本文介绍 本文给大家带来的CARAFE&#xff08;Content-Aware ReAssembly of FEatures&#xff09;是一种用于增强卷积神经网络特征图的上采样方法。其主要旨在改进传统的上采样方法&#xff08;就是我们的Upsample&#xff09;的性能。CARAFE的核心思想是&#xff1a;使用…

低代码优缺点的那些事

什么是低代码 低代码是一种软件开发方法,旨在通过使用可视化工具和组件库来简化应用程序开发。低代码平台提供了一种快速开发应用程序的方式,无需编写大量的代码。开发人员可以使用拖放界面和预构建组件来构建应用程序,从而加快开发速度和降低开发成本。低代码平台通常也提…

【LeetCode刷题笔记】155.最小栈

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 更多算法知识专栏&#xff1a;算法分析&#x1f525; 给大家跳段街舞感谢…

震惊,PDF文件转换已不再不是问题?

你是否曾经因为PDF文件的格式问题而感到困扰&#xff1f;是否曾经因为无法快速转换PDF文件而感到烦恼&#xff1f; 现在&#xff0c;这些问题都可以迎刃而解了&#xff01;下面这个在线PDF转换网站&#xff0c;就是你的解决方案。 目前5M以下文件免费转换&#xff0c;赶紧来看…

linux笔记--VSCode利用交换机跳转服务器

目录 1--前言 2--VSCode设置 3--ssh连接 1--前言 博主学校的服务器有两个&#xff0c;其中一个服务器&#xff08;14&#xff09;可以通过挂内网VPN来进行连接&#xff0c;但另一个服务器&#xff08;15&#xff09;即使挂了VPN也不能连接&#xff0c;只能通过内网进行连接。…

【机器学习】应用KNN实现鸢尾花种类预测

目录 前言 一、K最近邻&#xff08;KNN&#xff09;介绍 二、鸢尾花数据集介绍 三、鸢尾花数据集可视化 四、鸢尾花数据分析 总结 &#x1f308;嗨&#xff01;我是Filotimo__&#x1f308;。很高兴与大家相识&#xff0c;希望我的博客能对你有所帮助。 &#x1f4a1;本文由Fil…

MySQL 系列:注意 ORDER 和 LIMIT 联合使用的陷阱

文章目录 前言背后的原因ORDER BY 排序列存在相同值时返回顺序是不固定的LIMIT 和 ORDER BY 联合使用时的行为ORDER BY 或 GROUP BY 和 LIMIT 联合使用优化器默认使用有序索引 如何解决其它说明个人简介 前言 不知道大家在在分页查询中有没有遇到过这个问题&#xff0c;分页查…

三、JS逆向

一、JS逆向 解释&#xff1a;在我们爬虫的过程中经常会遇到参数被加密的情况&#xff0c;这样只有先在前端搞清楚加密参数是怎么生成的才能继续我们的爬虫&#xff0c;而且此时我们还需要用python去执行这个加密的过程。本文主要讲怎么在浏览器调试JS&#xff0c;以及Python执…

【数据结构和算法】--队列的特殊结构-循环队列

目录 循环队列的结构循环队列的实现循环队列的创建循环队列为空判断循环队列为满判断入队出队返回循环队列首元素返回循环队列尾元素释放循环队列 循环队列的结构 循环队列是队列的一种特殊结构&#xff0c;它的长度是固定的k&#xff0c;同样是先进先出&#xff0c;理论结构是…

PHP是世界上最好的语言-PolarDN XXF无参数RCE QUERY_STRING 特性

这个靶场我之前看到过打广告&#xff0c;而且感觉比较新 来坐坐 <?php //flag in $flag highlight_file(__FILE__); include("flag.php"); $c$_POST[sys]; $key1 0; $key2 0; if(isset($_GET[flag1]) || isset($_GET[flag2]) || isset($_POST[flag1]) || isset…

作者推荐 |【深入了解系统性能优化】「实战技术专题」全方面带你透彻探索服务优化技术方案(方案分析篇)

全方面带你透彻探索服务优化技术方案 前提背景影响一个系统性能的方方面面代码优化数据库优化网络优化硬件优化 常用的性能评价/测试指标响应时间并发数吞吐量响应时间、并发数和吞吐量之间的关系运作流程关系 性能优化方案的建议避免过早优化进行系统性能测试寻找系统瓶颈&…

Vue2将在2023年12月31日结束支持

文章目录 一、前言二、2023.12.31 会发生什么&#xff1f;三、接下来呢&#xff1f;四、仍然使用 Vue 2&#xff1f;你应该这样做4.1、升级到 Vue 2 的最终版本4.2、购买 Vue 2 的扩展支持4.3、通知用户 Vue 2 EOL 后的计划 五、展望未来六、最后 一、前言 随着 2024 年的临近…

【漏洞复现】捷诚管理信息系统 SQL注入漏洞

漏洞描述 捷诚管理信息系统是一款功能全面,可以支持自营、联营到外柜租赁的管理,其自身带工作流管理工具,能够帮助企业有效的开展内部审批工作。 该系统CWSFinanceCommon.asmx接口存在SQL注入漏洞。未经身份认证的攻击者可以通过该漏洞获取数据库敏感信息,深入利用可获取…

【5G PHY】5G小区类型、小区组和小区节点的概念介绍

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G算力网络技术标准研究。 博客…

谷歌浏览器标签页显示内存使用率

Chrome 桌面浏览器的新更新现在可让您查看每个标签页占用了多少内存&#xff0c;这可以帮助您确定哪些标签页占用了多少内存&#xff0c;网站正在减慢您笔记本电脑的速度。 今年早些时候在 Google Chrome 中引入内存节省程序之后&#xff0c;Google 又发布了一项功能&#xff…

【LeetCode刷题-树】--173.二叉搜索树迭代器

173.二叉搜索树迭代器 本题就是实现二叉树的中序遍历&#xff0c;利用数组本身实现迭代器 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.va…

栈和队列的实现(Java篇)

文章目录 一、栈的概念二、栈的实现2.1压栈(push)2.2出栈(pop)2.3获取栈顶元素(peek)2.4判断栈是否为空(isEmpty)栈的实现测试 三、队列的概念四、队列的实现4.1入队(offer)4.2出队(poll)4.3判断队列是否为空4.4获取对头元素队列的实现测试 五、循环队列5.1入队5.2出队5.3获取队…

基于Java SSM框架实现智能停车场系统项目【项目源码+论文说明】

基于java的SSM框架实现智能停车场系统演示 摘要 本论文主要论述了如何使用JAVA语言开发一个智能停车场管理系统&#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作者将论述…

TVS管连接方式与电压的选取

TVS管连接方式与电压的选取 电源供电电压为12V时&#xff0c;TVS管可以选用15V&#xff1b;电源供电电压为24V&#xff0c;TVS管可以选用24V。 TVS管的供电接口的连接方式。我们看到有些厂家的步进电机机驱动器或者其他驱动或做有防浪涌电路时&#xff0c;会有一个超级大的直插…

tomcat启动异常:子容器启动失败(a child container failed during start)

最近在使用eclipse启动Tomcat时&#xff0c;发现一个问题&#xff0c;启动以前的项目突然报子容器启动异常。 异常信息如下&#xff1a; 严重: 子容器启动失败 java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: 无法启动组件[org.apache.…