【hello Linux】可重入函数、volatile和SIGCHLD信号

news2024/11/25 20:43:50

目录

1. 可重入函数

2. volatile

3. SIGCHLD信号


Linux!🌷 

1. 可重入函数

先来谈一下重入函数的概念:重入函数便是在该函数还没有执行完毕便重复进入该函数(一般发生在多线程中);

可重入函数:一个函数一旦重入,对原函数功能不会出现问题(内存泄漏等);

不可重入函数:一个函数一旦重入,对原函数功能有可能出现问题;

什么意思呢?下面给一个具体的例子方便大家理解:

  • main函数调用insert函数向一个链表head中插入节点node1,插入操作分为两步,刚做完第一步的时候,因为硬件中断使进程切换到内核,再次回用户态之前检查到有信号待处理,于是切换到sighandler函数,sighandler也调用insert函数向同一个链表head中插入节点node2,插入操作的 两步都做完之后从sighandler返回内核态,再次回到用户态就从main函数调用的insert函数中继续 往下执行,先前做第一步之后被打断,现在继续做完第二步。结果是,main函数和sighandler先后向链表中插入两个节点,而最后只有一个节点真正插入链表中了。
  • 像上例这样,insert函数被不同的控制流程调用,有可能在第一次调用还没返回时就再次进入该函数,这称为重入,insert函数访问一个全局链表,有可能因为重入而造成错乱,像这样的函数称为不可重入函数,反之,如果一个函数只访问自己的局部变量或参数,则称为可重入(Reentrant) 函数。
如果一个函数符合以下条件之一则是不可重入的 :
  • 调用了mallocfree,因为malloc也是用全局链表来管理堆的。
  • 调用了标准I/O库函数。标准I/O库的很多实现都以不可重入的方式使用全局数据结构。

注意:可重入函数/不可重入函数,只是一种特性而已,并无优劣好坏之分;

2. volatile

volatile其实是C语言中的一个关键字,还是比较冷门的,虽然冷门但不意味着不重要;

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

下面给出几个例子方便大家理解:

示例一: 

  #include <stdio.h>    
  #include <signal.h>                                                                                                                                           
      
  int flag = 0;    
      
  void handler(int signo)    
  {    
    printf("change flag 0 to 1!\n");    
    flag = 1;    
  }    
      
  int main()    
  {    
    signal(2,handler);    
    while(!flag);    
    printf("process quit normal!\n");    
    return 0;    
  }    

标准情况下,键入 CTRL-C ,2号信号被捕捉,执行自定义动作,修改 flag=1 , while 条件不
满足,退出循环,进程退出;

示例二:gcc 进行一定程度的优化

对 makefile 文件进行修改再编译,-O3表示优化级数; 

优化情况下,键入 CTRL-C ,2号信号被捕捉,执行自定义动作,修改 flag=1 ,但是 while 条
件依旧满足,进程继续运行!但是很明显flag肯定已经被修改了,但是为何循环依旧执行?很
明显, while 循环检查的flag,并不是内存中最新的flag,这就存在了数据二异性的问题。
while 检测的flag其实已经因为优化,被放在了CPU寄存器当中。

示例三:volatile进行修饰

  #include <stdio.h>    
  #include <signal.h>    
      
  volatile int flag = 0;                                                                                                                           
      
  void handler(int signo)    
  {    
    printf("change flag 0 to 1!\n");    
    flag = 1;    
  }    
      
  int main()    
  {    
    signal(2,handler);    
    while(!flag);    
    printf("process quit normal!\n");    
    return 0;    
  }   

可以看到结果正确;

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

vloatile 还有一个作用:防止指令重排(一条C/C++指令进行编译后可能形成多条汇编代码,

volatile可保证多条汇编代码的顺序不会发生改变),这个了解下就好;

 3. SIGCHLD信号

进程一章讲过用 wait waitpid 函数清理僵尸进程 , 父进程可以阻塞等待子进程结束 , 也可以非阻
塞地查询是否有子进程结束等待清理(也就是轮询的方式) 。采用第一种方式 , 父进程阻塞了就
不能处理自己的工作了;采用第二种方式 , 父进程在处理自己的工作的同时还要记得时不时地轮
询一 下, 程序实现复杂。
其实, 子进程在终止时会给父进程发 SIGCHLD 信号 , 该信号的默认处理动作是忽略, 父进程
可以自定义 SIGCHLD 信号的处理函数, 这样父进程只需专心处理自己的工作, 不必关心子进
程了, 子进程终止时会通知父进程 , 父进程在信号处理函数中调用wait 清理子进程即可。
事实上 , 由于 UNIX 的历史原因 , 要想不产生僵尸进程还有另外一种办法 : 父进程调用 sigaction
SIGCHLD 的处理动作置为SIG_IGN, 这样 fork 出来的子进程在终止时会自动清理掉 , 不会产生
僵尸进程 , 也不会通知父进程。系统默认的忽略动作和用户用sigaction 函数自定义的忽略通常
是没有区别的 , 但这是一个特例。此方法对于 Linux 可用 , 但不保证在其它UNIX 系统上都可用。

下面编写代码对上述所说的内容进行一个验证:

代码1:子进程在退出时候会发送SIGCHLD信号?

#include <stdio.h>    
#include <unistd.h>    
#include <signal.h>    
                                                                                            
void handler(int signo)    
{    
  printf("get a signal,signo:%d\n",signo);    
}    
    
int main()    
{    
  //对信号捕捉    
  signal(SIGCHLD,handler);    
    
  //创建子进程    
  if(fork()==0)    
  {    
    //child    
    printf("I am a child,I quit...!\n");    
    sleep(1);    
    return 0;    
  }    
  //parent    
  while(1)    
  {    
    ;    
  }    
  return 0;    
}    

 经过证实,子进程在退出时会发送 17)SIGCHLD 信号;

代码2:可以在SIGCHLD自定义捕捉时,waitpid子进程?

#include <stdio.h>    
#include <signal.h>    
#include <unistd.h>    
#include <sys/types.h>    
#include <sys/wait.h>    
    
void handler(int signo)    
{    
  printf("get a signal!signo:%d\n",signo);    
  sleep(1);    
  while(1)    
  {    
    waitpid(-1,NULL,WNOHANG);    
  }    
}    
    
int main()    
{    
  //捕捉信号    
  signal(SIGCHLD,handler);    
  //创建子进程    
  if(fork()==0)    
  {    
    //child    
    printf("I am a child!\n");    
    sleep(1);    
    return 0;                                                         
  }    
  while(1);    
  return 0;    
}    

 由上我们可以看到子进程的3种状态的切换,S(在1S输出I am a child)——>Z(sleep1S后才waitpid)——>被清理;

代码3:直接在收到SIGCHLD时,进行忽略处理,不形成僵尸,直接处理;

#include <stdio.h>    
#include <signal.h>    
#include <unistd.h>    
#include <sys/types.h>    
#include <sys/wait.h>    
    
int main()    
{    
  //捕捉信号    
  signal(SIGCHLD,SIG_IGN);                                                             
  //创建子进程    
  if(fork()==0)    
  {    
    //child    
    printf("I am a child!\n");    
    sleep(1);    
    return 0;    
  }    
  while(1);    
  return 0;    
}    

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

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

相关文章

C++程序设计——lambda表达式

一、问题引入 在C98中&#xff0c;如果想对一个数据集合中的元素进行排序&#xff0c;可以使用sort()方法&#xff0c;但如果待排序元素为自定义类型&#xff0c;就需要用户自己定义排序时的比较规则。 随着C语法的发展&#xff0c;人们开始觉得其编写比较复杂&#xff0c;每次…

Word2vec原理+实战学习笔记(一)

来源&#xff1a;投稿 作者&#xff1a;阿克西 编辑&#xff1a;学姐 视频链接&#xff1a;https://ai.deepshare.net/detail/p_5ee62f90022ee_zFpnlHXA/6 文章标题&#xff1a; Efficient Estimation of Word Representations in Vector Space 基于向量空间中词表示的有效估计…

【计算机网络】学习笔记:第四章 网络层(七千字详细配图)【王道考研】

基于本人观看学习b站王道计算机网络课程所做的笔记&#xff0c;不做任何获利 仅进行交流分享 特此鸣谢王道考研 若有侵权请联系&#xff0c;立删 如果本篇笔记帮助到了你&#xff0c;还请点赞 关注 支持一下 ♡>&#x16966;<)!! 主页专栏有更多&#xff0c;如有疑问欢迎…

安装chatglm

地址 下载源代码 下载完成后解压 安装cuda 输入nvcc -V查看是否安装cuda 输入nvidia-smi查看支持的最高版本&#xff0c;最高支持12.1 下载cudahttps://developer.nvidia.com/cuda-downloads 双击安装 同意之后点击下一步 选择精简模式即可 等待下载安装包 …

链接sqlite

一.sqlite库函数 1.sqlite3_open()函数 语法&#xff1a;*sqlite3_open(const char *filename, sqlite3 *ppDb) 作用&#xff1a;该例程打开一个指向 SQLite 数据库文件的连接&#xff0c;返回一个用于其他 SQLite 程序的数据库连接对象。 参数1&#xff1a;如果 filename …

如何在自己的Maven工程上搭建Mybatis框架?

编译软件&#xff1a;IntelliJ IDEA 2019.2.4 x64 操作系统&#xff1a;win10 x64 位 家庭版 Maven版本&#xff1a;apache-maven-3.6.3 Mybatis版本&#xff1a;3.5.6 目录 前言 一. 什么是Mybatis框架&#xff1f;1.1 框架是什么&#xff1f;1.2 什么是MyBatis &#xff1f;1…

3.11 C结构体及结构体数组

结构体的意义 问题&#xff1a;学籍管理需要每个学生的下列数据&#xff1a;学号、姓名、性别、年龄、分数&#xff0c;请用C语言程序存储并处理一组学生的学籍。 思考&#xff1a;如果有多个学生&#xff0c;该怎么定义 已学数据类型无法解决。 结构体概述 正式&#xff1a;…

【Sping学习详解】

重新学习Spring很久了&#xff0c;也看了不少的视频&#xff0c;但是没有系统总结&#xff0c;容易忘记&#xff0c;网上寻找相关博客&#xff0c;也没有找到按照路线总结的&#xff0c;只能说不顺我心&#xff0c;所以自己总结一下&#xff01;&#xff01;&#xff01; 从下…

vulnhub靶机dpwwn1

准备工作 下载连接&#xff1a;https://download.vulnhub.com/dpwwn/dpwwn-01.zip 网络环境&#xff1a;DHCP、NAT 下载完后解压&#xff0c;然后用VMware打开dpwwn-01.vmx文件即可导入虚拟机 信息收集 主机发现 端口发现 继续查看端口服务信息 打开网站发现只有Apache默认…

【Spring篇】IOC/DI注解开发

&#x1f353;系列专栏:Spring系列专栏 &#x1f349;个人主页:个人主页 目录 一、IOC/DI注解开发 1.注解开发定义bean 2.纯注解开发模式 1.思路分析 2.实现步骤 3.注解开发bean作用范围与生命周期管理 1.环境准备 2.Bean的作用范围 3.Bean的生命周期 4.注解开发依赖…

行为识别 Activity Recognition

行为识别 行为检测是一个广泛的研究领域&#xff0c;其应用包括安防监控、健康医疗、娱乐等。 课程大纲 导论 图卷积在行为识别中的应用&#xff1a;论文研读&#xff0c;代码解读&#xff0c;实验 Topdown关键点检测中的hrnet&#xff1a;论文研读&#xff0c;代码解读&a…

ETL工具 - Kettle 流程、应用算子介绍

一、Kettle 流程和应用算子 上篇文章对Kettle 转换算子进行了介绍&#xff0c;本篇文章继续对Kettle 的流程和应用算子进行讲解。 下面是上篇文章的地址&#xff1a; ETL工具 - Kettle 转换算子介绍 流程算子主要用来控制数据流程和数据流向&#xff1a; 应用算子则是Kettle给…

ESP32 ESP-Rainmaker 本地点灯控制Demo测试

基于ESP-Rainmaker 本地点灯控制Demo测试 &#x1f33f;ESP-Rainmaker项目地址&#xff1a;https://github.com/espressif/esp-rainmaker/tree/master ✨这个项目早些时候就已经开始测试了&#xff0c;最后卡在了手机APP连接esp32设备端一直无法连接上&#xff0c;也一直没有找…

性能:Intel Xeon(Ice Lake) Platinum 8369B阿里云CPU处理器

阿里云服务器CPU处理器Intel Xeon(Ice Lake) Platinum 8369B&#xff0c;基频2.7 GHz&#xff0c;全核睿频3.5 GHz&#xff0c;计算性能稳定。目前阿里云第七代云服务器ECS计算型c7、ECS通用型g7、内存型r7等规格均采用该款CPU。 Intel Xeon(Ice Lake) Platinum 8369B Intel …

Linux第五章

文章目录 前言一、MySQL5.7版本在CentOS系统安装二、Tomcat安装部署1.安装JDK环境2. 解压并安装Tomcat 三、Nginx安装部署四、RabbitMQ安装部署五、Redis安装部署六、ElasticSearch安装部署七、集群化环境前置准备八、Zookeeper集群安装部署九、Kafka集群安装部署十、大数据集群…

操作系统的进程调度

进程调度概述 一、操作系统的调度时机 1、什么时候进行进程调度&#xff1f; 主动放弃&#xff08;进程正常终止、运行过程中发生异常而终止、进程主动请求阻塞&#xff09; 被动放弃&#xff08;分给进程的时间片用完、有更紧急的事需要处理、有更高优先级的进程进入就绪队列…

CentOS7 安装MySQL8

CentOS7 安装MySQL8 安装 VMware 以及 CentOS更新系统添加 MySQL Yum 存储库安装 MySQL 8启动 MySQL检查 MySQL 状态查看临时密码用临时密码登录修改密码 安装 VMware 以及 CentOS 由于本博主之前写过&#xff0c;在这给出链接&#xff0c;不再赘述了 https://blog.csdn.net/w…

2023.4.30 第五十一次周报

目录 前言 文献阅读 背景 对现有技术的分析 主要思路和贡献 相关性分析和归一化处理 相关性分析 归一化处理 TCN 基于 TCN-LSTM 的 PM 浓度预测模型 敏感性分析 论文思路 求半方差 训练模型 -1 训练模型-2 总结 前言 This week I studied an article that cons…

R语言 | 进阶字符串的处理

目录 一、语句的分割 二、修改字符串的大小写 三、unique()函数的使用 四、字符串的连接 4.1 使用paste()函数常见的失败案例1 4.2 使用paste()函数常见的失败案例2 4.3 字符串的成功连接与collapse参数 4.4 再谈paste()函数 4.5 扑克牌向量有趣的应用 五、字符串数据的…