volatile+SIGCHLD信号+可重入函数(了解)

索引

  • volatile
      • 1.gcc -O含义及其作用
      • 2.证明其内存可见性
  • 深入理解SIGCHLD信号
      • SIGCHLD总结
  • 可重入函数

volatile

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

1.gcc -O含义及其作用

gcc提供了为了满足用户不同程度的优化需求,提供了很多优化选项,用来对(编译时间,目标文件长度,执行效率)进行平衡。下面大致介绍几个优化级别

-O0:不做任何优化,这是默认的编译选项。
-O和-O1:对程序做部分编译优化,对于大函数,优化编译占用稍微多的时间和相当大的内存,编译器会尝试缩小代码的尺寸以及缩短执行时间,但并不执行需要占用大量编译器的优化。
-O2:比O1高级,进行更多的优化。Gcc将执行几乎所有的不包含时间和空间这种的优化,此时编译器不进行循环打开()以及内联函数,与O1比较而言,O2优化增加了编译时间的基础上,提高了生成代码的执行效率。
O3:在O2的基础上做进一步优化。

优化的代码可能给调试带来问题,可能会改变代码的结构,eg:对分支的合并和消除
还有可能给内存操作顺序改变带来问题。eg:对于某些依赖内存操作顺序而进行的逻辑,需要做严格的处理后才能进行优化

2.证明其内存可见性

此时程序的编译:
g++ -o $@ $^ -std=c++11 -O2

int flag = 0;
void handler(int signo)
{printf("变量的值已经修改了 flag:%d\n", flag);flag = 1;
}
int main()
{signal(2, handler);while (!flag);printf("process 正常退出\n");return 0;
}

在这里插入图片描述
上述代码Gcc编译时进行了O2优化,编译器编译的时候看到循环中没有语句,此时其循环的判断条件除第一次从内存中提取之外,其他时间都是从寄存器提取,因此,当我们CTRL+C时,命名条件的flag = 1,但程序依旧不会跳出循环。
如果我们在一样的代码上添加volatile
在这里插入图片描述
由此可见volatile的作用其保持内存的可见性.

深入理解SIGCHLD信号

父进程创建子进程后,之前讲述的都是父进程用waitpid()和wiat()函数清理僵尸进程,此时既可以阻塞式等待:此时父进程无法进一步处理自己的工作,直到子进程退出。
非阻塞式等待:父进程采取轮循检测的方式等待子进程退出。
实际上在子进程退出的时候会给父进程发送一个信号:SIGCHLD信号,只不过父进程对该信号的默认处理方式是忽略。下面验证一下子进程退出的时候,子进程是否会发送SIGCHLD信号。
实验设计:
父进程fork()创建子进程之后,子进程exit()退出,给SIGCHLD信号设计自定义捕捉函数。

void ChildHandler(int signo)
{// 此时阻塞式等待任何一个子进程。pid_t id = waitpid(-1, nullptr, 0);if (id > 0){cout << "父进程等待成功" << endl;}
}
int main()
{signal(SIGCHLD, ChildHandler);pid_t id = fork();if (id == 0){cout << "我是子进程" << endl;sleep(3);exit(1);}while (true){cout << "main running..." << endl;sleep(1);}return 0;
}

在这里插入图片描述
其实上述的自定义捕捉函数是有bug的,如果此时父进程创建了很多子进程并且子进程同时退出的话,就会有多个SIGCHLD信号被发送,会导致该信号被堵塞,此时就会产生很多僵尸进程

void ChildHandler(int signo)
{// 此时阻塞式等待任何一个子进程。pid_t id = waitpid(-1, nullptr, 0);if (id > 0){cout << "父进程等待成功" << endl;}
}
int main()
{signal(SIGCHLD, ChildHandler);for (int i = 0; i < 10; i++){pid_t id = fork();if (id == 0){int cnt = 10;while (cnt){cout << "我是子进程,pid: " << getpid() << "  cnt:" << cnt-- << endl;sleep(1);}cout << "子进程退出进入僵尸状态;" << endl;exit(1);}}while (true){cout << "main running..." << endl;sleep(1);}return 0;
}

在这里插入图片描述

由于信号同时在处理时继续发送信号此时信号会被阻塞,所以上述代码会出现僵尸进程。
所以我们循环式读取直到waitpid()调用失败,此时跳出循环表示已经全部等待完成,就不会出现僵尸进程。

void ChildHandler(int signo)
{while (true){// 此时阻塞式等待任何一个子进程。pid_t id = waitpid(-1, nullptr, 0);if (id > 0){cout << "父进程等待成功" << endl;}}
}

在这里插入图片描述
此时并不会出现僵尸进程,但是此情况针对的是子进程同时退出,子进程也有可能不同时退出。如下所示。

void ChildHandler(int signo)
{while (true){// 此时阻塞式等待任何一个子进程。pid_t id = waitpid(-1, nullptr, 0);if (id > 0){cout << "父进程等待成功" << endl;}}
}
int main()
{signal(SIGCHLD, ChildHandler);for (int i = 0; i < 10; i++){pid_t id = fork();if (id == 0){int cnt = 10;if (i < 6){cnt = 5;}else{cnt = 50;}while (cnt){cout << "我是子进程,pid: " << getpid() << "  cnt:" << cnt-- << endl;sleep(1);}cout << "子进程退出进入僵尸状态;" << endl;exit(1);}}while (true){cout << "main running..." << endl;sleep(1);}return 0;
}

在这里插入图片描述
此时就会出现子进程一直在运行,但是父进程不会运行自己的代码,因为此时自定义函数中的等待方式是阻塞式等待,此时只要我们将阻塞式等待变成非阻塞式等待即可。

void ChildHandler(int signo)
{while (true){// 此时非阻塞式等待任何一个子进程。pid_t id = waitpid(-1, nullptr, WNOHANG);if (id > 0){cout << "父进程等待成功 child pid:" << getpid() << endl;}else if (id == 0){cout << "还有子进程没有退出,父进程要去忙自己的事情了" << endl;break;}else{cout << "父进程等待所有的子进程结束" << endl;break;}}
}
int main()
{signal(SIGCHLD, ChildHandler);for (int i = 0; i < 10; i++){pid_t id = fork();if (id == 0){int cnt = 10;if (i < 6){cnt = 5;}else{cnt = 20;}while (cnt){cout << "我是子进程,pid: " << getpid() << "  cnt:" << cnt-- << endl;sleep(1);}cout << "子进程退出进入僵尸状态;" << endl;exit(1);}}while (true){cout << "我是父进程,我正在运行:" << getpid() << endl;cout << "main running..." << endl;sleep(1);}return 0;
}

在这里插入图片描述

SIGCHLD总结

上述是使用该信号的自定义捕捉函数,那么如果我们不使用他呢?
sigactionSIGCHLD的处理动作设置成SIG_IGN(忽略),这样fork()出来的子进程在终止时会自动清理掉,不会产生僵尸进程,也不会通知父进程,系统默认的忽略动作和用户用sigaction函数自定义的忽略通常是没有区别的,但这是一个特例,该方法只对Linux可用,但不保证在其他的UNIX系统上都可用。
这里就不做模拟了。
但是设置了SIG_IGN后,可不仅仅是自动释放子进程这一个内容,这是一个函数,函数可以修改父进程的相关属性,进而被子进程继承下去,如果没有设置忽略的话,子进程在没有waitpid()的情况下是会产生僵尸进程的,但是设置SIG_IGN就不会产生僵尸进程,子进程会自动释放!

可重入函数

当两个不同的控制流程调用同一个函数,访问同一个局部变量或参数造成程序的错乱,该函数就是不可冲入函数。
符合下列条件之一的就是不可冲入函数:

1.调用了malloc 或 free,因为malloc也是用全局链表来管理堆的
2.调用了标准I/O库函数,标准I/O库的很多实现都以不可重入的方式使用全局数据结构。

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

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

相关文章

DSP_TMS320F28377D_算法加速方法4_C语言编程优化

前面3篇的优化思路是从硬件本身和函数库这些方向去加速&#xff0c; 本文则仅从代码本身的效率去考虑加速的方法。 1、用全局变量比用局部变量快 void testfunction1(){ // 局部变量int i;double s,a,b;a 1.023;b 12.23;for(i 0; i < 1000; i){s __divf32(a,b);} }int …

消灭怪物的最大数量【力扣1921】

一、题目分析 需要满足的条件&#xff1a; 只能在每分钟的开始使用武器武器能杀死距离城市最近的怪兽怪兽到达城市就会输掉游戏 游戏最优策略&#xff1a;我们可以在每分钟的开始都使用一次武器&#xff0c;用来杀死距离城市最近的怪兽。这样可以在力所能及的范围内&#xf…

Lesson4-3:OpenCV图像特征提取与描述---SIFT/SURF算法

学习目标 理解 S I F T / S U R F SIFT/SURF SIFT/SURF算法的原理&#xff0c;能够使用 S I F T / S U R F SIFT/SURF SIFT/SURF进行关键点的检测 SIFT/SURF算法 1.1 SIFT原理 前面两节我们介绍了 H a r r i s Harris Harris和 S h i − T o m a s i Shi-Tomasi Shi−Tomasi…

JVM垃圾回收机制和常用算法(简洁版)

垃圾收集 (Garbage Collection,GC) 垃圾收集主要是针对堆和方法区进行。程序计数器、虚拟机栈和本地方法栈这三个区域属于线程私有的&#xff0c;只存在于线程的生命周期内&#xff0c;线程结束之后就会消失&#xff0c;因此不需要对这三个区域进行垃圾回收。 判断一个对象是…

QT day5

服务器&#xff1a; #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);//给服务器指针实例化对象server new QTcpServer(this); }Widget::~Widget() {delete ui…

vue3中TCplayer应用

环境win10:vitevue3elementUI 1 安装 npm install tcplayer.js2 使用 <template><div><video id"player-container-id" width"414" height"270" preload"auto" playsinline webkit-playsinline></video>&l…

【数据结构练习】栈的面试题集锦

目录 前言&#xff1a; 1.进栈过程中可以出栈的选择题 2.将递归转化为循环 3.逆波兰表达式求值 4.有效的括号 5. 栈的压入、弹出序列 6. 最小栈 前言&#xff1a; 数据结构想要学的好&#xff0c;刷题少不了&#xff0c;我们不仅要多刷题&#xff0c;还要刷好题&#x…

大模型 Dalle2 学习三部曲(二)clip学习

clip论文比较长48页&#xff0c;但是clip模型本身又比较简单&#xff0c;效果又奇好&#xff0c;正所谓大道至简&#xff0c;我们来学习一下clip论文中的一些技巧&#xff0c;可以让我们快速加深对clip模型的理解&#xff0c;以及大模型对推荐带来革命性的变化。 clip结构 首选…

Python+Selenium4环境搭建

很久没有了解自动化了&#xff0c;最近发现项目中沉淀了很多东西&#xff0c;回归测试效率很低&#xff0c;所以必须要考虑构建自动化来提供各个环节的小效率。由于忙于需求以及产品的流程规范&#xff0c;现在对于测试技术方面的研究也相对少了很多。不过不管做什么&#xff0…

Arduino驱动LX1972传感器(光照传感器篇)

LX1972环境光(可见光)传感器,对可见光照度的反应特性与人眼的特性类似,可以模拟人对环境光线的强度的判断,从而方便做出与人友好互动的应用,可应用于照明控制、屏幕背光控制等。 1、传感器特性 传感器技术指标如下: 工作温度: -40~80C照度范围: 1 – 800Lux输出信号:…

CSS中如何实现文字描边效果(Text Stroke)?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 文字描边效果&#xff08;Text Stroke&#xff09;⭐ 示例⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个…

nas汇编程序的调试排错方法

nas汇编程序的调试排错方法&#xff1a; 1、查找是哪一步错了 2、查看对应的*.lst文件&#xff0c;本例中是"asmhead.lst" 3、根据*.lst文件的[ERROR #002]提示查看源码&#xff0c;改错。 4、重新运行编译&#xff0c;OK 1、查找是哪一步错了&#xff1a; nask.ex…

黑盒测试方法论—边界值

边界值分析法是一种很实用的黑盒测试用例方法&#xff0c;它具有很强的发现故障的能力。边界值分析法也是作为对等价类划分法的补充&#xff0c;测试用例来自等价类的边界。 这个方法其实是在测试实践当中发现&#xff0c;Bug 往往出现在定义域或值域的边界上&#xff0c;而不…

结构体(个人学习笔记黑马学习)

1、结构体的定义和使用 #include <iostream> using namespace std; #include <string>struct Student {string name;int age;int score; }s3;int main() {//1、struct Student s1;s1.name "张三";s1.age 18;s1.score 100;cout << "姓名&a…

css 选择器

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><style>/* ~ 表示的h…

FPGA输出lvds信号点亮液晶屏

1 概述 该方案用于生成RGB信号&#xff0c;通过lvds接口驱动逻辑输出&#xff0c;点亮并驱动BP101WX-206液晶屏幕。 参考&#xff1a;下面为参考文章&#xff0c;内容非常详细。Xilinx LVDS Output——原语调用_vivado原语_ShareWow丶的博客http://t.csdn.cn/Zy37p 2 功能描述 …

如何在你的Android工程中启用K2编译器?

如何在你的Android工程中启用K2编译器&#xff1f; K2编译器是用于Kotlin代码编译的最新、高效编译器&#xff0c;你现在可以尝试使用了。 Kotlin编译器正在为Kotlin 2.0进行重写&#xff0c;新的编译器实现&#xff08;代号K2&#xff09;带来了显著的构建速度改进&#xff…

10 mysql tiny/small/medium/big int 的数据存储

前言 这里主要是 由于之前的一个 datetime 存储的时间 导致的问题的衍生出来的探究 探究的主要内容为 int 类类型的存储, 浮点类类型的存储, char 类类型的存储, blob 类类型的存储, enum/json/set/bit 类类型的存储 本文主要 的相关内容是 int 类类型的相关数据的存储 …

Vue3 学习 组合式API setup语法糖 响应式 指令 DIFF(一)

文章目录 前言一、Composition Api二、setup语法糖三、响应式refreactive 四、其他一些关键点v-prev-oncev-memov-cloak 五、虚拟Dom五、diff算法 前言 本文用于记录学习Vue3的过程 一、Composition Api 我觉得首先VUE3最大的改变就是对于代码书写的改变&#xff0c;从原来选择…

购物商场项目实践

1.项目开始 1&#xff09;此项目为在线电商项目 2&#xff09;包含首页&#xff0c;搜索列表&#xff0c;商品详情&#xff0c;购物车&#xff0c;订单&#xff0c;支付&#xff0c;用户登录/注册等多个子模块 3&#xff09;使用Vue全家桶ES6webpackAxios等前端技术 4&…