linux_时序竞态-pause函数-sigsuspend函数-异步I/O-可重入函数-不可重入函数
接上一篇:linux_信号捕捉-signal函数-sigaction函数-sigaction结构体
今天来分享时序竞态的知识,关于时序竞态的问题,肯定会和cpu有关,也会学习两个函数,pause函数,sigsuspend函数, 也会分享什么是可重入函数和不可重入函数,话不多说,上一碗时序竞态的大菜:
此博主在CSDN发布的文章目录:【我的CSDN目录,作为博主在CSDN上发布的文章类型导读】
在介绍时序竞态之前,先介绍一下pause函数。
1.pause函数
函数作用:
调用该函数可以造成进程主动挂起,等待信号唤醒。调用该系统调用的进程将处于阻塞状态(主动放弃cpu) 直到有信号递达将其唤醒。
头文件:
#include <unistd.h>
函数原型:
int pause(void);
函数参数:
无
返回值:
返回值:-1 并设置errno为EINTR
① 如果信号的默认处理动作是终止进程,则进程终止,pause函数么有机会返回。
② 如果信号的默认处理动作是忽略,进程继续处于挂起状态,pause函数不返回。
③ 如果信号的处理动作是捕捉,则【调用完信号处理函数之后,pause返回-1】
errno设置为EINTR,表示“被信号中断”。想想我们还有哪个函数只有出错返回值。
④ pause收到的信号不能被屏蔽,如果被屏蔽,那么pause就不能被唤醒。
1.1.例子–pause函数运用:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
void donothing(int signo)
{
}
unsigned int mysleep(unsigned int seconds)
{unsigned int ret;struct sigaction act, oldact;act.sa_handler = donothing;sigemptyset(&act.sa_mask);//信号集清零act.sa_flags = 0;
//注册信号捕捉函数sigaction(SIGALRM, &act, &oldact);alarm(seconds); //定时固定的秒数 1 pause(); //挂起ret = alarm(0); sigaction(SIGALRM, &oldact, NULL); //恢复SIGALRM 默认处理方式return ret;
}
int main(void)
{mysleep(5);return 0;
}
2.时序竞态
时序竞态: 由于进程之间执行的顺序不同,导致同一个进程多次运行后产生了不同结果的现象。
竞态问题总结:
竞态条件,跟系统负载有很紧密的关系,体现出信号的不可靠性。系统负载越严重,信号不可靠性越强。
不可靠由其实现原理所致。信号是通过软件方式实现(跟内核调度高度依赖,延时性强),每次系统调用结束后,或中断处理处理结束后,需通过扫描PCB中的未决信号集,来判断是否应处理某个信号。当系统负载过重时,会出现时序混乱。
这种意外情况只能在编写程序过程中,提早预见,主动规避,而无法通过gdb程序调试等其他手段弥补。且由于该错误不具规律性,后期捕捉和重现十分困难。
3.时序竞态问题1-信号处理
为什么会有时序竞态的问题产生,是因为cpu在执行进程的时候,一个进程只执行一个时间片段,所以,你写的程序运行的时候,有时候执行千次万次看似没什么问题,可是某一次突然就崩了,当你去查问题的时候,复查了很长的时间,都没有找到问题,这种问题的出现的概念可能是千万分之一,不容易发生,但一发生就是致命问题,而这种问题还不易发现,只能通过我们日常写代码的经验来避免。
例如在1.1的例子中,在调用alarm函数后,失去CPU,CPU去执行别的进程了,当执行别的进程的时间大于定时的时间后,会发生什么问题, 如下图。

就这样,本能定时1s的程序,成了永久阻塞了,这种情况,是可能发生的,而发生的几率,可能就是千万分之一。
设想在商业代码中出现这种错误,那后果则是毁灭性的。
当然,在上述案例中,也有解决方法,那就是利用信号的屏蔽机制来解决,这就得说一下另一个函数sigsuspend了。
3.1.解决时序问题1-sigsuspend函数
函数作用:
挂起等待信号。
头文件:
#include <signal.h>
函数原型:
int sigsuspend(const sigset_t *mask);
函数参数:
mask:调用该函数期间决定信号屏蔽字得集合
返回值:
错误返回-1,并设置errno以指示错误(通常为EINTR)。
EINTR:被一个信号中断。
可以通过设置屏蔽SIGALRM的方法来控制程序执行逻辑,但无论如何设置,程序都有可能在“解除信号屏蔽”与“挂起等待信号”这个两个操作间隙失去cpu资源。除非将这两步骤合并成一个“原子操作”。sigsuspend函数具备这个功能。在对时序要求严格的场合下都应该使用sigsuspend替换pause。
原子操作:cpu在执行这个函数就会把他执行完,不会停止
3.2.例子-解决例1.1时序竞态问题1代码:
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
void sig_alrm(int signo)
{/* nothing to do */
}
unsigned int mysleep(unsigned int nsecs)
{struct sigaction newact, oldact;sigset_t newmask, oldmask, suspmask;unsigned int unslept;/*为SIGALRM设置捕捉函数,一个空函数*/newact.sa_handler = sig_alrm;//将信号集清零sigemptyset(&newact.sa_mask);newact.sa_flags = 0;//注册信号捕捉函数,oldact保留原有的信号集sigaction(SIGALRM, &newact, &oldact);/*设置阻塞信号集,阻塞SIGALRM信号*/sigemptyset(&newmask);//将信号集清零sigaddset(&newmask, SIGALRM);//将SIGALRM信号加入信号集,置1//屏蔽SIGALRM信号,设置信号屏蔽字,oldmask保留原有的信号集sigprocmask(SIG_BLOCK, &newmask, &oldmask); //原子操作,即调用该函数期间不能失去cpu//定时nsecs秒,到时后可以产生SIGALRM信号alarm(nsecs);/*构造一个调用sigsuspend临时有效的阻塞信号集,* 在临时阻塞信号集里解除SIGALRM的阻塞*/suspmask = oldmask; //sigdelset(&suspmask, SIGALRM); //在suspmask集合中清除对SIGALRM函数的屏蔽/*sigsuspend调用期间,采用临时阻塞信号集suspmask替换原有阻塞信号集* 这个信号集中不包含SIGALRM信号,同时挂起等待,* 当sigsuspend被信号唤醒返回时,恢复原有的阻塞信号集*/sigsuspend(&suspmask); unslept = alarm(0);//恢复SIGALRM原有的处理动作,呼应前面注释1sigaction(SIGALRM, &oldact, NULL);//解除对SIGALRM的阻塞,呼应前面注释2sigprocmask(SIG_SETMASK, &oldmask, NULL);return(unslept);
}
int main(void)
{
while(1)
{mysleep(2);printf("Two seconds passed\n");}return 0;
}
4.时序竞态问题2-全局变量异步I/O
分析如下父子进程交替数数程序。
当捕捉函数里面的sleep取消,程序即会出现问题。
造成该问题出现得原因是什么呢?
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>int n = 0, flag = 0;
void sys_err(char *str)
{perror(str);exit(1);
}
void do_sig_child(int num)
{printf("I am child %d\t%d\n", getpid(), n);n += 2;flag = 1;sleep(1);
}
void do_sig_parent(int num)
{printf("I am parent %d\t%d\n", getpid(), n);n += 2;flag = 1;sleep(1);
}
int main(void)
{pid_t pid;
struct sigaction act;if ((pid = fork()) < 0)sys_err("fork");else if (pid > 0) { n = 1;sleep(1);act.sa_handler = do_sig_parent;sigemptyset(&act.sa_mask);act.sa_flags = 0;sigaction(SIGUSR2, &act, NULL); //注册自己的信号捕捉函数 父使用SIGUSR2信号do_sig_parent(0); while (1) {/* wait for signal */;if (flag == 1) { //父进程数数完成kill(pid, SIGUSR1);flag = 0; //标志已经给子进程发送完信号}}} else if (pid == 0) { n = 2;act.sa_handler = do_sig_child;sigemptyset(&act.sa_mask);act.sa_flags = 0;sigaction(SIGUSR1, &act, NULL);while (1) {/* waiting for a signal */;if (flag == 1) {kill(getppid(), SIGUSR2);flag = 0;//分析,若是在cpu执行到此处时,收到父进程得信号,在flag还未被改完,就去执行do_sig_child该函数,会怎么样?}}}return 0;
}
示例中,通过flag变量标记程序实行进度。flag置1表示数数完成。flag置0表示给对方发送信号完成。
问题出现的位置,在父子进程kill函数之后需要紧接着调用 flag,将其置0,标记信号已经发送。但,在这期间很有可能被kernel调度,失去执行权利,而对方获取了执行时间,通过发送信号回调捕捉函数,从而修改了全局的flag。
如何解决该问题呢?
可以使用后续会分享到的“锁”机制。 当操作全局变量的时候,通过加锁、解锁来解决该问题。
现在,我们在编程期间如若使用全局变量,应在主观上注意全局变量的异步IO可能造成的问题。
5.时序竞态问题3-可/不可重入函数
一个函数在被调用执行期间(尚未调用结束),由于某种时序又被重复调用,称之为“重入”。根据函数实现的方法可分为“可重入函数”和“不可重入函数”两种。
可重入函数:函数内不能含有全局变量及static变量,不能使用malloc、free等。
不可重入函数:函数内含有全局变量及static变量,使用malloc、free,是标准I/O函数。
所以,我们的信号捕捉函数应该设计为可重入函数。
信号处理程序可以调用的可重入函数可参阅man 7 signal。
以上就是本次的分享了,希望能对广大网友有所帮助。
相关文章:
linux_时序竞态-pause函数-sigsuspend函数-异步I/O-可重入函数-不可重入函数
接上一篇:linux_信号捕捉-signal函数-sigaction函数-sigaction结构体 今天来分享时序竞态的知识,关于时序竞态的问题,肯定会和cpu有关,也会学习两个函数,pause函数,sigsuspend函数, 也会分享什么…...
Tomcat的负载均衡和动静分离
---------------------NginxTomcat负载均衡、动静分离------------------------- Nginx 服务器:192.168.80.10:80 Tomcat服务器1:192.168.80.100:80 Tomcat服务器2:192.168.80.101:8080 192.168.80.101:8081 1.部署Nginx 负载均衡器 system…...
C++每日一练:最长递增区间 阿波罗的魔力宝石 投篮
文章目录 前言一、最长递增区间二、阿波罗的魔力宝石三、投篮总结 前言 今天的题太简单,甚至 “最长递增区间” 和 “投篮” 就是一个问题。实在没事干,也给做了!直接上代码算了… 提示:以下是本篇文章正文内容 一、最长递增区间…...
HCIP之VLAN
目录 网络的三层架构 接入层 无线的缺陷: 上网用户数量增多,网络卡顿的原因 CSMA/CD --- 载波侦听多路访问/冲突检测 CSMA/CA --- 载波侦听多路访问/冲突避免 无线网络没有使用冲突检测技术的原因 汇聚层 连接两条线路的原因 核心层 VLAN VLAN配…...
1686_MATLAB处理Excel文件
全部学习汇总: GreyZhang/g_matlab: MATLAB once used to be my daily tool. After many years when I go back and read my old learning notes I felt maybe I still need it in the future. So, start this repo to keep some of my old learning notes servral …...
亿发软件:中大型仓库进出货管理系统解决方案,定制软件让仓储作业高效便捷
中大型仓库出入库管理是传统厂家供应链管理流程的重要部分,直接关乎货物在仓库当中存储的安全,和员工工作的效率。一旦仓库管理当中出现了疏漏,那么货物的信息数据就会发生变动,导致实际与账目不符。人工带来的低效与不可控是传统…...
SQL Server基础 第二章 表结构管理
目录 一、数据类型 1,字符类数据类型 2,数值型数据类型 3,日期/时间型数据类型 二、主键(Primary key) 三、默认值 四、唯一键(Unique) 五、自增标识 六、约束 七、外键 一、数据类型 …...
华为OD机试真题(Java),最小步骤数(100%通过+复盘思路)
一、题目描述 一个正整数数组 设为nums,最大为100个成员,求从第一个成员开始正好走到数组最后一个成员所使用的最小步骤数。 要求: 第一步 必须从第一元素起 且 1<第一步步长<len/2 (len为数组长度);从第二步开始只能以所…...
3分钟搞懂:JavaScript 和 ECMAScript
JavaScript 和 ECMAScript ECMAScript 是 JavaScript 语言的国际标准,JavaScript 是 ECMAScript 的一种实现(Adobe ActionScript 和 JScript 同样实现了 ECMAScript)。 ECMAScript 是欧洲计算机制造商协会 ECMA(European Comput…...
Bito:一款 iead/webstorm 神级插件,由 ChatGPT 团队开发,堪称辅助神器
前言: idea(后端),webstorm(前端)中可以用的一款辅助插件:Bito 个人尝试体验效果: 优点是:可以自动完成一些场景代码。 缺点:太慢了,大部分时间一直转圈 摘取文档: 什么是Bito&…...
[ 云原生 | Docker ] 构建高可用性的 SQL Server:Docker 容器下的主从同步实现指南
文章目录 一、前言二、SQL Server 主从同步的原理介绍三、具体的搭建过程3.1 准备工作3.1.1 卸载旧版本(如果有,可选,非必须)3.1.2 安装 Docker3.1.3 验证本地 Docker 是否安装成功 3.2 创建 Docker 网络3.3 创建主从节点的 SQL S…...
Binary Utilities非默认目录构建和安装
在AppArmor零知识学习六、源码构建(3)中,详细介绍了libapparmor的构建步骤,但那完全使用的是官网给出的默认参数。如果需要将目标文件生成到指定目录而非默认的/usr,则需要进行一些修改,本文就来详述如何进…...
【故障检测】基于 KPCA 的故障检测【T2 和 Q 统计指数的可视化】(Matlab代码实现)
💥 💥 💞 💞 欢迎来到本博客 ❤️ ❤️ 💥 💥 🏆 博主优势: 🌞 🌞 🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 …...
从“捐赠openEuler”到“向openEuler捐赠”,openEuler生态走入高速发展期
【中国,上海,2023年4月21日】openEuler Developer Day 2023于4月20-21日在线上和线下同步举办。本次大会由开放原子开源基金会指导,中国软件行业协会、openEuler社区、边缘计算产业联盟共同主办,以“万涓汇流,奔涌向前…...
ambari的kafka服务开启sasl
添加 sasl 配置⽂件 集群部署 Kafka2.2下载地址 http://archive.apache.org/dist/kafka/2.2.1/kafka_2.11-2.2.1.tgz 解压安装包 tar -zxvf kafka_2.11-2.2.1.tgz 部署略 ambari 数据kafka服务 在kafka的conf目录下创建sasl_conf目录,将kafka_client_jaas.conf/kafka_se…...
改善内部客户服务的 3 个技巧
在当今世界,许多公司都专注于改善客户关系管理,公司管理层面临的挑战是他们不仅拥有外部客户,员工也是有痛点和需求的内部客户。正如糟糕的客户服务会导致客户流失一样,糟糕的内部客户服务会增加员工流动率。在当今瞬息万变的就业…...
使用Apache POI的SXSSFworkbook实现大量数据导出到Excel文件
Apache POI是一个开源的、用来读写微软Excel文件的Java工具包,一般用来读取Excel文件中的数据或者将数据导出到Excel文件。HSSFWorkbook用来处理早期版本的Excel文件(xls格式),而XSSFWorkbook用来处理新版本Excel文(xl…...
【技术选型】Java 定时任务
文章目录 背景一、基础1.1 Cron表达式1.2 定时任务的三大组成部分 二、Java做定时任务的技术方案比较2.1、JDK seelp实现定时任务2.2、JDK Timer & TimerTask 实现定时任务2.3、JDK ScheduledExecutorService2.4、Quartz框架2.5、Spring Task 中的 schedule2.6、Elastic-Jo…...
让你立刻学会指针
☃️个人主页:fighting小泽 🌸作者简介:目前正在学习C语言和数据结构 🌼博客专栏:C语言学习 🏵️欢迎关注:评论👊🏻点赞👍🏻留言💪&am…...
重塑元宇宙体验!元宇宙实时云渲染解决方案来了
元宇宙作为人工智能、云计算和数字孪生等前沿技术的结合体,近年来越发受到各大企业重视。 元宇宙的应用场景层出不穷,不仅包括营销推广场景,还有品牌活动和电商销售,能有效提升品宣和商业转化效果。 元宇宙也具有极大的建设价值…...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
C++ 设计模式 《小明的奶茶加料风波》
👨🎓 模式名称:装饰器模式(Decorator Pattern) 👦 小明最近上线了校园奶茶配送功能,业务火爆,大家都在加料: 有的同学要加波霸 🟤,有的要加椰果…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能
1. 开发环境准备 安装DevEco Studio 3.1: 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK 项目配置: // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...
从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障
关键领域软件测试的"安全密码":Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力,从金融交易到交通管控,这些关乎国计民生的关键领域…...
redis和redission的区别
Redis 和 Redisson 是两个密切相关但又本质不同的技术,它们扮演着完全不同的角色: Redis: 内存数据库/数据结构存储 本质: 它是一个开源的、高性能的、基于内存的 键值存储数据库。它也可以将数据持久化到磁盘。 核心功能: 提供丰…...
