原子操作和竞争条件

所有系统调用都是以原子操作方式执行的。之所以这么说,是指内核保证了某系统调用中的所有步骤会作为独立操作而一次性加以执行,其间不会为其他进程或线程所中断。原子性是某些操作得以圆满成功的关键所在。特别是它规避了竞争状态(race conditions)(有时也称为竞争冒险)。

竞争状态是这样一种情形:操作共享资源的两个进程(或线程),其结果取决于一个无法预期的顺序,即这些进程1 获得 CPU 使用权的先后相对顺序。接下来,将讨论涉及文件 I/O 的两种竞争状态,并展示了如何使用 open()的标志位,来保证相关文件操作的原子性,从而消除这些竞争状态。

以独占方式创建一个文件

当同时指定 O_EXCL 与 O_CREAT 作为 open()的标志位时,如果要打开的文件已然存在,则 open()将返回一个错误。这提供了一种机制,保证进程是打开文件的创建者。对文件是否存在的检查和创建文件属于同一原子操作。要理解这一点的重要性,以下代码,并未使用 O_EXCL 标志。

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>void errExit(const char* msg) {perror(msg);exit(EXIT_FAILURE);
}int main(int argc, char *argv[]) {int fd;if (argc < 2) {fprintf(stderr, "Usage: %s <file>\n", argv[0]);exit(EXIT_FAILURE);}fd = open(argv[1], O_WRONLY); // Try to open the fileif (fd != -1) { // Open succeededprintf("[%ld] File \"%s\" already exists\n", (long)getpid(), argv[1]);close(fd);} else {if (errno != ENOENT) { // Failed for unexpected reasonerrExit("open");} else { // File does not exist, try to create itfd = open(argv[1], O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);if (fd == -1) {errExit("open");}printf("[%ld] Created file \"%s\" exclusively\n", (long)getpid(), argv[1]);close(fd);}}return EXIT_SUCCESS;
}

程序尝试以只写模式打开文件,如果文件不存在(errno 设置为 ENOENT),则尝试创建文件。然而,这里存在一个竞态条件(race condition)的问题。

问题在于,程序在检查文件是否存在(通过尝试打开它)和实际创建文件之间有一个时间窗口,在这个时间窗口内,其他进程可能会创建该文件。这意味着即使第一次调用 open 失败,表明文件当时不存在,第二次调用 open 时文件可能已经被另一个进程创建了,这违反了独占性的要求。

内核调度器判断出分配给 A 进程的时间片已经耗尽,并将 CPU 使用权交给 B 进程,就可能会发生这种问题。再比如两个进程在一个多CPU 系统上同时运行时,也会出现这种情况。

下图 展示了两个进程同时以上执行程序代码的情形。在这一场景下,进程 A 将得出错误的结论:目标文件是由自己创建的。因为无论目标文件存在与否,进程 A 对 open()的第二次调用都会成功。虽然进程将自己误认为文件创建者的可能性相对较小,但毕竟是存在的,这已然将此段代码置于不可靠的境地。操作的结果将依赖于对两个进程的调度顺序,这一事实也就意味着出现了竞争状态。

在这里插入图片描述

为了说明这段代码的确存在问题,对上述代码进一步改造一下更能说明问题,在检查文件是否存在与创建文件这两个动作之间人为制造一个长时间的等待.

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>void errExit(const char* msg) {perror(msg);exit(EXIT_FAILURE);
}int main(int argc, char *argv[]) {int fd;if (argc < 2) {fprintf(stderr, "Usage: %s <file>\n", argv[0]);exit(EXIT_FAILURE);}fd = open(argv[1], O_WRONLY); // Try to open the fileif (fd != -1) { // Open succeededprintf("[%ld] File \"%s\" already exists\n", (long)getpid(), argv[1]);close(fd);} else {if (errno != ENOENT) { // Failed for unexpected reasonerrExit("open");} else { // File does not existprintf("[%ld] File \"%s\" doesn't exist yet\n", (long)getpid(), argv[1]);if (argc > 2) {sleep(5); // Suspend execution for 5 secondsprintf("[%ld] Done sleeping\n", (long)getpid());}// Attempt to create the filefd = open(argv[1], O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);if (fd == -1) {errExit("open");}printf("[%ld] Created file \"%s\" exclusively\n", (long)getpid(), argv[1]);close(fd);}}return EXIT_SUCCESS;
}

两个进程都会声称自己以独占方式创建了文件。由于第一个进程在检查文件是否存在和创建文件之间发生了中断,造成两个进程都声称自己是文件的创建者。结合 O_CREAT 和 O_EXCL 标志来一次性地调用 open()可以防止这种情况,因为这确保了检查文件和创建文件的步骤属于一个单一的原子(即不可中断的)操作。
在这里插入图片描述

一种可以正确的做法是:

fd = open(argv[1], O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
if (fd == -1) {if (errno == EEXIST) {printf("[%ld] File \"%s\" already exists\n", (long)getpid(), argv[1]);} else {// Handle other errorserrExit("open");}
} else {printf("[%ld] Created file \"%s\" exclusively\n", (long)getpid(), argv[1]);
}

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

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

相关文章

arm内核驱动-中断

先介绍个东西 ctags 这个工具可以像keil一样在工程里查找跳转&#xff0c;帮我们找到我们想要的东西。 安装教程可以找到&#xff0c;这里只讲怎么用。 在工程目录&#xff08;包含所有你会用到的头文件等&#xff09;下&#xff0c;先加载这个命令&#xff0c;可能要等待…

repl_backlog原理

2.2.3.repl_backlog原理 master怎么知道slave与自己的数据差异在哪里呢? 这就要说到全量同步时的repl_baklog文件了。 这个文件是一个固定大小的数组&#xff0c;只不过数组是环形&#xff0c;也就是说角标到达数组末尾后&#xff0c;会再次从0开始读写&#xff0c;这样数组…

vue快速入门(十三)v-model的使用

注释很详细&#xff0c;直接上代码 上一篇 新增内容 数据双向绑定数据清空方法 源码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-…

inline内联函数速解

inline内联的效果 #include <stdio.h>inline int max(int x, int y) {return x > y ? x : y; }int main() {int a 5, b 10;int m max(a, b);printf("The maximum value is %d\n", m);return 0; }当编译器决定将max函数内联时&#xff0c;它会在编译时期…

vue数据检测原理

前言 Vue中的数据监听离不开Object.defineProperty()方法的使用&#xff0c;在了解数据监测原理之前&#xff0c;建议先掌握defineProperty的用法。 目标 1 数据监测问题 2 数据监测原理 3 如何实现数组更新 1 遇到的问题 数组更新问题 <button click"updatePeople&q…

ABAP-CPI-Odata POST-create_deep_entity 多层嵌套的处理及CPI端的调用

该文章演示怎么在OData里,创建一个多套多的请求结构,传入数据处理后,返回多层级的处理结果;以及如何在CPI里写groovy脚本,去解析它;最后如何用postman模拟外围系统,调用CPI这个接口,从而去调用Odata接口 假如想用SAP Odata去实现传入多层级的数据,进行创建或者根据传入…

【pb_ds】2024.4.11

2024.4.11 【虚怀若谷&#xff0c;戒骄戒躁。】 Thursday 三月初三 <theme oi-“language”> 这个好东西叫pb_ds!!! #include<bits/extc.h> using namespace __gnu_cxx; using namespace __gnu_pbds;堆 操作/数据结构配对堆二叉堆左偏树二项堆斐波那契堆代码p…

libVLC 提取视频帧使用QWidget渲染

在前面的文章中&#xff0c;我们使用libvlc_media_player_set_hwnd设置了视频的显示的窗口。 libvlc_media_player_set_hwnd(vlc_mediaPlayer, (void *)ui.widgetShow->winId()); 如果我们想要提取每一帧数据&#xff0c;将数据渲染到QWidget上&#xff0c;该如何操作呢&a…

java八股——消息队列MQ

上一篇传送门&#xff1a;点我 目前只学习了RabbitMQ&#xff0c;后续学习了其他MQ后会继续补充。 MQ有了解过吗&#xff1f;说说什么是MQ&#xff1f; MQ是Message Queue的缩写&#xff0c;也就是消息队列的意思。它是一种应用程序对应用程序的通信方法&#xff0c;使得应用…

爬虫之获取人人车网站中车辆的信息

爬虫之获取人人车网站中车辆的信息 import base64 import timeimport pymysql from fontTools.ttLib import TTFont from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.edge.options import Options import recar_id_list…

后端项目部署教程

一、打包jar包 lyamanoblog-server-0.0.1.jar ps:运行时可能会提醒不能有大写字母&#xff0c;所以用的都是小写字母 二、编写Dockerfile文件 FROM java:8 VOLUME /tmp ADD lyamanoblog-server-0.0.1.jarblog.jar ENTRYPOINT ["java","-Djava.securit…

Codeforces Round 142 (Div. 2) - B. T-primes (数论)

我们知道&#xff0c;质数是具有两个不同正除数的正整数。同样&#xff0c;我们把正整数 t t t 称为质数。Т-质数&#xff0c;如果 t t t 恰好有三个不同的正除数。 给你一个由 n 个正整数组成的数组。请判断其中每个整数是否为 Т-prime。 输入 第一行包含一个正整数 n …

八次危机笔记

文章目录 前言一、思维导图危机一危机二危机三危机四危机五危机六危机七危机八 前言 重塑三观&#xff0c;致敬温老。一个有良心的学者&#xff01;&#xff01;&#xff01; 一、思维导图 危机一 危机二 危机三 危机四 危机五 危机六 危机七 危机八 ☆

2023年上半年信息系统项目管理师——综合知识真题与答案解释(1)

2023年上半年信息系统项目管理师 ——综合知识真题与答案解释(1) 零、00时光宝盒 1009 Rejections 1009 拒绝 Once, there was an old man, who was broke, living in a tiny house and owned a beat-up car. 有一次&#xff0c;有一个老人&#xff0c;他破产了&#…

【LeetCode刷题笔记】LeetCode 1365.有多少小于当前数字的数字

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

curaengine编译源码之libarcus编译记录

libArcus的编译&#xff08;成功安装&#xff09; This library contains C code and Python3 bindings for creating a socket in a thread and using this socket to send and receive messages based on the Protocol Buffers library. It is designed to facilitate the c…

从文字到思维:呆马GPT在人工智能领域的创新之旅

引言 生成式预训练变换器&#xff08;Generative Pre-trained Transformer&#xff0c;简称GPT&#xff09;领域是人工智能技术中的一大革新。自OpenAI推出第一代GPT以来&#xff0c;该技术经历了多代发展&#xff0c;不断提升模型的规模、复杂度和智能化程度。GPT模型通过在大…

网工内推 | 网安、AGV测试网络工程师,厂商认证优先,应届可投

01 神州数码 招聘岗位&#xff1a;网络工程师 职责描述&#xff1a; 1、负责国内外主流安全产品&#xff08;如防火墙、入侵防御、WAF、安全审计等&#xff09;的上线安装、调试、测试、割接、运维等工作。 2、能够独立进行安全类项目实施、问题排查及处理。 3、在出现网络攻…

Python 推导式介绍

Python推导式是一种简洁而强大的语法&#xff0c;用于在一行代码中创建集合&#xff08;list、set、dictionary&#xff09;的方式。推导式使得代码更加简洁易读&#xff0c;提高了代码的可读性和可维护性。Python中有列表推导式、集合推导式和字典推导式三种类型。 列表推导式…

力扣日记4.10-【动态规划篇】343. 整数拆分

力扣日记&#xff1a;【动态规划篇】343. 整数拆分 日期&#xff1a;2024.4.10 参考&#xff1a;代码随想录、力扣 343. 整数拆分 题目描述 难度&#xff1a;中等 给定一个正整数 n &#xff0c;将其拆分为 k 个 正整数 的和&#xff08; k > 2 &#xff09;&#xff0c;并…