【Linux】可重入函数 volatile关键字 以及SIGCHLD信号

可重入函数 volatile关键字 以及SIGCHLD信号

  • 一、可重入函数
    • 1、引入
    • 2、可重入函数的判断
  • 二、volatile关键字
    • 1、引入
    • 2、关于编译器的优化的简单讨论
  • 三、SIGCHLD信号

一、可重入函数

1、引入

我们来先看一个例子来帮助我们理解什么是可重入函数:

假设我们现在要对一个链表进行头插,在执行到第10行代码时,突然进程的时间片到了,进程被切换了,一会等进程再度切换回来时,当前进程要处理信号,而信号处理函数是sighandler,而sighandler里面也进行了头插,等进程从内核态返回到用户态时,继续执行第11行的代码,这时我们再观察链表的结构会发现链表中出现了节点丢失的问题,而造成这种问题的根源是我们的insert函数同时被两个执行流给进入了。

node_t node1, node2, *head;
int main()
{...insert(&node1);...
}void insert(node_t*p)
{p->next = head;head = p;
}void sighandler(int signo)
{insert(&node2);
}

在这里插入图片描述

由这个问题衍生出了一种函数分类的方式:

  • 如果一个函数同时被多个执行流进入所产生的结果没有问题,该函数被称为可重入函数
  • 如果一个函数同时被多个执行流进入所产生的结果有问题,该函数被称为不可重入函数
  • 可重入函数主要用于多任务环境中,一个可重入的函数通常来说就是可以被中断的函数,也就是说,可以在这个函数执行的任何时刻中断它,转入OS调度下去执行另外一段代码,而返回控制时不会出现什么错误;
  • 不可重入的函数由于使用了一些系统资源,比如全局变量区,中断向量表等,所以它如果被中断的话,可能会出现问题,这类函数是不能运行在多任务环境下的。

2、可重入函数的判断

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

  1. 函数体内使用了静态(static)的数据结构或者变量;
  2. 调用了mallocfree,因为malloc也是用全局链表来管理堆的。
  3. 调用了标准I/O库函数。标准I/O库的很多实现都以不可重入的方式使用全局数据结构。

二、volatile关键字

1、引入

volatile是C语言的一个关键字,该关键字的作用是保证内存数据的可见性

我们来先来看一段代码,这里我们不加入volatile关键字并开启编译器优化选项,优化级别是-O2

这段代码的意思是:我们让进程一直运行,直到我们给进程发送2号信号以后,进程再退出。

#include <stdio.h>
#include <unistd.h>
#include <signal.h>int flag = 0;void handler(int signo)
{printf("捕捉到了%d号信号\n", signo);// 将flag置为1flag = 1;printf("已经将flag置为%d\n", flag);
}int main()
{signal(2, handler);printf("进程正在运行...\n");while (!flag);  // 当flag == 1时,进程退出。printf("运行结束!\n");return 0;
}

运行结果:

在这里插入图片描述
可以看到,我们明明都已经让flag = 1了但是进程中的循环依然没有结束,这时为什么呢?下面我们一起来分析这个过程:


代码中的main函数和handler函数在触发时是两个独立的执行流,而while循环是在main函数当中的,而且main执行流里面并没有使用过handler函数(signal函数只是对2号信号进行了捕捉,没有调用过handler函数),所以在编译器编译时检测到在main函数中对flag变量没有做过修改操作,而且由于while循环运行时需要频繁使用flag变量,所以编译器可以将flag变量的值用一个寄存器进行保存,以后每次使用flag变量直接去寄存器里面取数据,不必每次都要将内存中的flag搬运到寄存器里面然后让CPU去计算。

可是不巧的是我们给当前进程发送了2号信号,让另外一个执行流更改了内存中的flag变量,而由于编译器的优化,认为flag变量不会改变导致内存中的flag变量改变以后也没有将寄存器中的数据同步修改,而CPU运算使用的数据又是寄存器中的数据,这就导致了内存数据的不可见,于是while循环就会一直运行,导致了上面的问题。

在这里插入图片描述

为了让编译器每次都要去内存取数据来进行计算,我们可以在flag变量前面加上volatile关键字。

#include <stdio.h>
...
volatile int flag = 0;void handler(int signo)
{...
}
int main()
{...
}

再次运行程序,发现运行结果符合预期!

在这里插入图片描述

2、关于编译器的优化的简单讨论

上面的代码如果我们不开启优化,就算不加上volatile关键字也是能正常运行的,可见编译器的优化不是越高越好。

如何理解编译器的优化?

编译器的本质是将代码翻译成01的二进制序列,所以编译器的优化是在你编写的代码上动手脚,也就是说编译器的优化其实改变了一些最终翻译成01二进制以后的执行逻辑。

三、SIGCHLD信号

在一前我们讲过用waitwaitpid函数清理僵尸进程,父进程可以阻塞等待子进程结束,也可以非阻塞地查询是否有子进程结束等待清理(也就是轮询的方式)。采用第一种方式,父进程阻塞了就不能处理自己的工作了;采用第二种方式,父进程在处理自己的工作的同时还要记得时不时地轮询一 下,也很麻烦。
《wait与waitpid的使用介绍》

上面使用waitwaitpid其实都是父进程主动检查子进程是否处于僵尸状态,那么有没有一种方法能够让子进程主动告诉父进程自己处于僵尸状态呢?

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

下面就是一个对SIGCHLD信号的一个使用:

在父进程中我们创建了10个子进程,这10个子进程退出时都会给父进程发送SIGCHLD信号,由于父进程回收其中一个子进程时,其他子进程也有可能同时给父进程发送SIGCHLD信号,而pending表又没有办法同时存储多个信号,所以我们就要进行循环回收子进程,而为了不影响父进程的执行流程我们可以选择非阻塞等待。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>pid_t id = 0;void WaitProcess(int signo)
{printf("捕捉到了%d号信号,正在处理...\n", signo);while (1){pid_t ret = waitpid(-1, NULL, WNOHANG);if (ret > 0){printf("等待子进程%d成功,父进程%d\n", ret, id);}else{break;}}printf("WaitProcess, done\n");
}int main()
{signal(SIGCHLD, WaitProcess);int i = 0;// 创建10个子进程for (i = 0; i < 10; i++){id = fork();// 子进程if (id == 0){int cnt = 5;//睡眠cnt秒以后退出while (cnt--){printf("我是子进程,我的pid是:%d,ppid是:%d\n", getpid(), getppid());sleep(1);}exit(0);}}// 父进程一直休眠while (1){sleep(1);}return 0;
}

在这里插入图片描述

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

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>pid_t id = 0;int main()
{// 对SIGCHLD设置为忽略,这样产生的子进程退出时不会形成僵尸状态。signal(SIGCHLD, SIG_IGN);int i = 0;// 创建10个子进程for (i = 0; i < 10; i++){id = fork();// 子进程if (id == 0){int cnt = 5;//睡眠cnt秒以后退出while (cnt--){printf("我是子进程,我的pid是:%d,ppid是:%d\n", getpid(), getppid());sleep(1);}exit(0);}}// 父进程一直休眠while (1){sleep(1);}return 0;
}

在这里插入图片描述

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

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

相关文章

EthGlobal 巴黎站 Chainlink 获奖项目介绍

在 Web3 中&#xff0c;每一周都至关重要。项目的发布、版本的发布以及协议的更新以惊人的速度推出。开发者必须保持学习&#xff0c;随时了解最新的工具&#xff0c;并将所有他们所学的东西&#xff08;无论是旧的还是新的&#xff09;联系起来&#xff0c;以构建推动 Web3 技…

PLUS操作流程、应用与实践,多源不同分辨率数据的处理、ArcGIS的应用、PLUS模型的应用、InVEST模型的应用

PLUS模型是由中国地质大学&#xff08;武汉&#xff09;地理与信息工程学院高性能空间计算智能实验室开发&#xff0c;是一个基于栅格数据的可用于斑块尺度土地利用/土地覆盖(LULC)变化模拟的元胞自动机(CA)模型。PLUS模型集成了基于土地扩张分析的规则挖掘方法和基于多类型随机…

Word转PDF在线转换如何操作?分享转换技巧

现如今&#xff0c;pdf转换器已成为大家日常办公学习必不可少的工具&#xff0c;市场上的pdf转换器主要有两种类型&#xff0c;一种是需要下载安装的&#xff0c;另一种是网页版&#xff0c;打开就可以使用的&#xff0c;今天小编给大家推荐一个非常好用的网页版pdf转换器&…

基于jvm-sandbox的imock开发指南

基于jvm-sandbox的imock开发指南 团队今年的指标是为公司提供一个方法级的mock平台&#xff0c; 这个重要的任务落在了我的身上。 0、明确团队的需求 支持java后端服务方法级别的mock&#xff0c;对没有测试环境的第三方服务进行mock&#xff0c;增加团队覆盖率。 启用&#x…

PDF换行的难度,谁能解决?

换行的时候确认不了长度&#xff1a; import java.awt.*;public class Test {public static void main(String[] args) {String str1 "淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘";String str2 "AAAAAAAAAAAAAAAAAAAAAAAAA…

实验篇——亚细胞定位

实验篇——亚细胞定位 文章目录 前言一、亚细胞定位的在线网站1. UniProt2. WoLFPSORT3. BUSCA4. TargetP-2.0 二、代码实现1. 基于UniProt&#xff08;不会&#xff09;2. 基于WoLFPSORT后续&#xff08;已完善&#xff0c;有关代码放置于[python爬虫学习&#xff08;一&#…

一零七零、Redis基础稳固篇

Redis是什么&#xff0c;优缺点&#xff1f; Redis本质是一个K-V类型的内存数据库 纯内存操作&#xff0c;每秒可处理超过10w的读写操作 优点&#xff1a; 读写性能极高 非阻塞IO 单线程 支持持久化 支持事务 数据结构丰富 缺点&#xff1a; 容易受到物理内存的限制 主机宕机可…

【JAVA】日志

输出语句日志输出位置只能是控制台可以将日志信息写入文件或数据库中取消日志需要修改代码&#xff0c;灵活性差只需修改日志文件多线程性能较差性能较好 日志规范接口&#xff1a;Commons Logging(JCL)、Simple Logging Facade for Java(slf4j) 日志实现框架&#xff1a;Log…

软件测试简历撰写与优化,让你面试邀约率暴增99%!

如何撰写一份优秀的简历呢&#xff1f;&#xff1f;这是一个求职者都会遇到的问题&#xff0c;今天就来详细带大家写一份软件测试工程师职位的简历&#xff01;希望能给各位软件测试求职者一个带来帮助&#xff01; 个人简历是求职者给招聘单位发的一份简要介绍。包含自己的基本…

linux系统服务学习(一)Linux高级命令扩展

文章目录 Linux高级命令&#xff08;扩展&#xff09;一、find命令1、find命令作用2、基本语法3、*星号通配符4、根据文件修改时间搜索文件☆ 聊一下Windows中的文件时间概念&#xff1f;☆ 使用stat命令获取文件的最后修改时间☆ 创建文件时设置修改时间以及修改文件的修改时间…

【Vue】Vue2创建移动端项目实战教程,创建移动端项目保姆级教程,设置axios,utils工具包,vue.fonfig.js配置项 (下)

系列文章目录 这里是创建移动端项目 【Vue】Vue2.x创建项目全程讲解&#xff0c;保姆级教程&#xff0c;手把手教&#xff0c;Vue2怎么创建项目&#xff08;上&#xff09; 【Vue】Vue2创建移动端项目实战教程&#xff0c;创建移动端项目保姆级教程&#xff0c;接上一篇创建Vue…

2023牛客暑期多校训练营9 B.Semi-Puzzle: Brain Storm

文章目录 题目大意题解求解回溯 参考代码 题目大意 给定两个数 a , m a,m a,m &#xff0c;求满足 a u ≡ u ( m o d m ) a^u \equiv u (mod\ \ m) au≡u(mod m) 的一个解。 ( 1 ≤ a , m ≤ 1 0 9 , 0 ≤ u ≤ 1 0 18 ) (1\leq a,m \leq10^9 ,0\leq u\leq 10^{18}) (1≤a…

玩赚音视频开发高阶技术——FFmpeg

随着移动互联网的普及&#xff0c;人们对音视频内容的需求也不断增加。无论是社交媒体平台、电商平台还是在线教育&#xff0c;都离不开音视频的应用。这就为音视频开发人员提供了广阔的就业机会。根据这些年来网站上的音视频开发招聘需求来看&#xff0c;音视频开发人员的需求…

如何优雅的使用Mock Server

事出有因 昨天跟同事讨论我们在用的rap2(一个集接口编写和mock server的开源项目)和刚上线了一个easy-mock的server&#xff0c;到底哪个好用。 我们主要讨论的点有个两个&#xff1a; 接口的一致性、 编码的无侵入性。 背景 自从前后端分离后&#xff0c;完成前后端的分工…

【计算机视觉|生成对抗】条件生成对抗网络(CGAN)

本系列博文为深度学习/计算机视觉论文笔记&#xff0c;转载请注明出处 标题&#xff1a;Conditional Generative Adversarial Nets 链接&#xff1a;[1411.1784] Conditional Generative Adversarial Nets (arxiv.org) 摘要 生成对抗网络&#xff08;Generative Adversarial…

Windows 11 家庭中文版找不到组策略文件gpedit.msc

最近因为调整日期问题需要用到组策略文件gpedit.msc,但是发现找不到文件 在按键盘 winR 打开运行界面输入 gpedit.msc 回车 Windows找不到文件’gpedit.msc’。请确定文件名是否正确后&#xff0c;再试-次。 检查电脑Windows系统版本 是 Windows 11 家庭中文版 果断早网上搜…

C++模板元编程入门案例

C++模板元编程(Template Metaprogramming)是一种在编译时进行计算和代码生成的技术,它使用C++的模板机制来实现。 下面是一个简单的C++模板元编程的示例,展示了如何在编译时计算一个数的阶乘。 #include <iostream> template <int N> struct Factorial { …

docker 学习--02 常用命令

docker 学习–02 常用命令 文章目录 docker 学习--02 常用命令1. 帮助启动类命令1.1启动docker1.2 停止docker1.3 重启docker1.4 查看docker1.5 设置开机自启1.6 查看docker概要信息1.7 查看docker总体帮助文档1.8 查看docker命令帮助文档 2. 镜像命令2.1 列出本地主机上有的镜…

Jmeter 参数化的几种方法

目录 配置元件-用户自定义变量 前置处理器-用户参数 配置元件-CSV Data Set Config Tools-函数助手 配置元件-用户自定义变量 可在测试计划、线程组、HTTP请求下创建用户定义的变量 全局变量&#xff0c;可以跨线程组调用 jmeter执行的时候&#xff0c;只获取一次&#xff0…

kafka 02——三个重要的kafka客户端

kafka 02——三个重要的kafka客户端 1. 前言1.1 关于 Kafka 的安装1.2 常用客户端简介1.3 依赖 2. AdminClient2.1 Admin Configs2.2 AdminClient API2.2.1 设置 AdminClient 对象2.2.2 创建 topic 获取 topic 列表2.2.3 删除topic2.2.4 查看 topic 的描述信息2.2.5 查看 topi…