深入探究linux文件IO

news2024/11/15 13:44:25

一、原子操作和竞争条件

  1. 所有系统调用都是以原子操作方式执行的。之所以这么说,是指内核保证了某系统调用中的所有步骤会作为独立操作而一次性加以执行,其间不会为其他进程或线程所中断。

  2. 以独占方式创建一个文件

  • 结合 O_CREAT 和 O_EXCL 标志来一次性地调用 open()可以防止这种情况,因

为这确保了检查文件和创建文件的步骤属于一个单一的原子(即不可中断的)操作。

  1. 向文件尾部追加数据

  • 将文件偏移量的移动与数据写操作纳入同一原子操作。在打开文件时加入 O_APPEND 标志就可以保证这一点。

二、文件控制操作:fcntl()

  1. fcntl()系统调用对一个打开的文件描述符执行一系列控制操作

#include <unistd.h> 
#include <fcntl.h> 
int fcntl(int fd, int cmd, ... /* arg */ );

2.fcntl中的cmd:

cmd指令

含义

示例

F_GETFL

获取其访问模式和状态标志

int flags,accessMode;flags = fcntl(fd,F_GETFL);if(flags & O_SYNC) ....

F_SETFL

修改打开文件的某些状态标志

允许更改的标志有O_APPEND、 O_NONBLOCK、 O_NOATIME、O_ASYNC 和 O_DIRECT

  • 判定文件的访问模式有一点复杂,这是因为 O_RDONLY(0)、O_WRONLY(1)和 O_RDWR(2)这 3 个常量并不与打开文件状态标志中的单个比特位对应。因此,要判定访问模式,需使用掩码 O_ACCMODE 与 flag 相与,将结果与 3 个常量进行比对,示例代码如下:

accessMode = flags & O_ACCMODE; 
if(accessMode == O_WRONLY || accessMode == O_RDWR) 
//....
使用 fcntl()修改文件状态标志,尤其适用于如下场景。
  • 文件不是由调用程序打开的,所以程序也无法使用 open()调用来控制文件的状态标志。

  • 文件描述符的获取是通过 open()之外的系统调用。比如 pipe()调用,该调用创建一个管道,并返回两个文件描述符分别对应管道的两端。再比如 socket()调用,该调用创建一个套接字并返回指向该套接字的文件描述符。

三、文件描述符和打开文件之间的关系

  1. 要理解具体情况如何,需要查看由内核维护的 3 个数据结构。

  2. 进程级的文件描述符表。

  3. 系统级的打开文件表。

  4. 文件系统的 i-node 表。

  5. 针对每个进程,内核为其维护打开文件的描述符(open file descriptor)表。该表的每一条目都记录了单个文件描述符的相关信息,如下所示

  6. 控制文件描述符操作的一组标志。(目前,此类标志仅定义了一个,即 close-on-exec 标志)。

  7. 对打开文件句柄的引用。

  8. 内核对所有打开的文件维护有一个系统级的描述表格(open file description table)。有时,也称之为打开文件表(open file table),并将表中各条目称为打开文件句柄(open file handle)。一个打开文件句柄存储了与一个打开文件相关的全部信息,如下所示:

  9. 当前文件偏移量(调用 read()和 write()时更新,或使用 lseek()直接修改)。

  10. 打开文件时所使用的状态标志(即,open()的 flags 参数)。

  11. 文件访问模式(如调用 open()时所设置的只读模式、只写模式或读写模式)。

  12. 与信号驱动 I/O 相关的设置。

  13. 对该文件 i-node 对象的引用。

  14. 每个文件系统都会为驻留其上的所有文件建立一个 i-node 表:

  15. 文件类型(例如,常规文件、套接字或 FIFO)和访问权限。

  16. 一个指针,指向该文件所持有的锁的列表。

  17. 文件的各种属性,包括文件大小以及与不同类型操作相关的时间戳。

  1. 上图揭示出如下要点:

  2. 两个不同的文件描述符,若指向同一打开文件句柄,将共享同一文件偏移量。因此, 如果通过其中一个文件描述符来修改文件偏移量(由调用 read()、write()或 lseek() 所致),那么从另一文件描述符中也会观察到这一变化。无论这两个文件描述符分属于不同进程,还是同属于一个进程,情况都是如此。

  3. 要获取和修改打开的文件标志(例如,O_APPEND、O_NONBLOCK 和 O_ASYNC),可 执行 fcntl()的 F_GETFL 和 F_SETFL 操作,其对作用域的约束与上一条颇为类似。

  4. 相形之下,文件描述符标志(亦即,close-on-exec 标志)为进程和文件描述符所私有。 对这一标志的修改将不会影响同一进程或不同进程中的其他文件描述符。

四、复制文件描述符

1. dup()调用

#include <unistd.h>   
int dup(int oldfd); 
int dup2(int oldfd, int newfd); 

2. dup()调用复制一个打开的文件描述符 oldfd,并返回一个新描述符,二者都指向同一打开 的文件句柄。系统会保证新描述符一定是编号值最低的未用文件描述符。

3. dup2()系统调用会为 oldfd 参数所指定的文件描述符创建副本,其编号由 newfd 参数指定。 如果由 newfd 参数所指定编号的文件描述符之前已经打开,那么 dup2()会首先将其关闭。

4. fcntl()的 F_DUPFD 操作是复制文件描述符的另一接口,更具灵活性

//将使用大于等于startfd的最小未用值作为描述符编号newfd = fcntl(oldfd,F_DUPFD,startfd);

  • 该调用还能保证新描述符(newfd)编号落在特定的区间范围内。

  • 文件描述符的正、副本之间共享同一打开文件句柄所含的文件偏移量和状 态标志。然而,新文件描述符有其自己的一套文件描述符标志,且其 close-on-exec 标志 (FD_CLOEXEC)总是处于关闭状态。

  • Linux 从 2.6.24 开始支持 fcntl()用于复制文件描述符的附加命令:F_DUPFD_CLOEXEC。 该标志不仅实现了与 F_DUPFD 相同的功能,还为新文件描述符设置 close-on-exec 标志。

5. dup3()系统调用完成的工作与 dup2()相同,只是新增了一个附加参数 flag,这是一个可以 修改系统调用行为的位掩码。

#define _GNU_SOURCE             /* See feature_test_macros(7) */ #include <fcntl.h>              /* Obtain O_* constant definitions */ #include <unistd.h>   int dup3(int oldfd, int newfd, int flags);
  • 目前,dup3()只支持一个标志 O_CLOEXEC,这将促使内核为新文件描述符设置 close-on-exec标志(FD_CLOEXEC)。

五、在文件特定偏移量处的I/O: pread()和pwrite()

  1. pread()和pwrite()

#include <unistd.h> 
ssize_t pread(int fd, void *buf, size_t count, off_t offset); ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);

六、分散输入和集中输出(Scatter-Gather I/O):readv() 和 writev()

  1. readv()和 writev()系统调用分别实现了分散输入和集中输出的功能

  2. #include <sys/uio.h> 
    ssize_t readv(int fd, const struct iovec *iov, int iovcnt); ssize_t writev(int fd, const struct iovec *iov, int iovcnt); ssize_t preadv(int fd, const struct iovec *iov, int iovcnt,                 off_t offset); ssize_t pwritev(int fd, const struct iovec *iov, int iovcnt,                 off_t offset); struct iovec {     void  *iov_base;    /* Starting address */ size_t iov_len;     /* Number of bytes to transfer */ };
  • readv()系统调用实现了分散输入的功能:从文件描述符 fd 所指代的文件中读取一片连续 的字节,然后将其散置(“分散放置”)于 iov 指定的缓冲区中。这一散置动作从 iov[0]开始, 依次填满每个缓冲区。

  • 原子性是 readv()的重要属性。换言之,从调用进程的角度来看,当调用 readv()时,内核 在 fd 所指代的文件与用户内存之间一次性地完成了数据转移。这意味着,假设即使有另一进 程(或线程)与其共享同一文件偏移量,且在调用 readv()的同时企图修改文件偏移量,readv() 所读取的数据仍将是连续的。

  • 调用 readv()成功将返回读取的字节数,若文件结束将返回 0。调用者必须对返回值进行 检查,以验证读取的字节数是否满足要求。若数据不足以填充所有缓冲区,则只会占用部分缓冲区,其中最后一个缓冲区可能只存有部分数据

struct iovec iov[3]; fd = open(argv[1], O_RDONLY); if (fd == -1)     errExit("open"); totRequired = 0; iov[0].iov_base = &myStruct; iov[0].iov_len = sizeof(struct stat); totRequired += iov[0].iov_len; iov[1].iov_base = &x; iov[1].iov_len = sizeof(x); totRequired += iov[1].iov_len; iov[2].iov_base = str; iov[2].iov_len = STR_SIZE; totRequired += iov[2].iov_len; numRead = readv(fd, iov, 3); if (numRead == -1) errExit("readv"); if (numRead < totRequired) 
printf("Read fewer bytes than requested\n");

七、截断文件:truncate()和ftruncate()系统调用

#include <unistd.h> #include <sys/types.h> //若文件当前长度大于参数 length,调用将丢弃超出部分, //若小于参数 length,调用将在文件尾部添加一系列空字节或是一个文件空洞。 int truncate(const char *path, off_t length); int ftruncate(int fd, off_t length);

八、非阻塞I/O

  1. 在打开文件时指定 O_NONBLOCK 标志,目的有二:

  2. 若 open()调用未能立即打开文件,则返回错误,而非陷入阻塞。有一种情况属于例外, 调用 open()操作 FIFO 可能会陷入阻塞。

  3. 调用 open()成功后,后续的 I/O 操作也是非阻塞的。若 I/O 系统调用未能立即完成,则可能会只传输部分数据,或者系统调用失败,并返回 EAGAIN或EWOULDBLOCK 错误。具体返回何种错误将依赖于系统调用。Linux 系统与许多 UNIX 实现一样,将两 个错误常量视为同义。

九、大文件 I/O

  1. 应用程序可使用如下两种方式之一以获得 LFS 功能。:

  • 在编译应用程序时,将宏_FILE_OFFSET_BITS 的值定义为 64。这一方法更为可取,因 为符合 SUS 规范的应用程序无需修改任何源码即可获得 LFS 功能。

十、/dev/fd 目录

  1. 对于每个进程,内核都提供有一个特殊的虚拟目录/dev/fd。该目录中包含“/dev/fd/n”形 式的文件名,其中 n 是与进程中的打开文件描述符相对应的编号。因此,例如, /dev/fd/0 就对应于进程的标准输入。

  2. 打开/dev/fd 目录中的一个文件等同于复制相应的文件描述符,所以下列两行代码是等价的:

fd = open("/dev/fs/1",O_WRONLY); fd = dup(1);
 

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

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

相关文章

AI学习记录 - 怎么理解 torch 的 torch.nn.BatchNorm2d

画图不易&#xff0c;有用就点个赞 这里创建了一个随机张量&#xff0c;形状为 (4, 3, 4, 4)&#xff0c;分别对应 形状为 (batch_size, num_channels, height, width) batch_size&#xff1a;批次 num_channels&#xff1a; 通道&#xff08;什么是通道看上一章节&#xff0…

串口和RS485通信

一、 定义串口收发数据结构体 /*COM Received Data Structure*/ typedef struct {uint8_t ubr_EndFlag; //Received data end flag uint8_t ubr_buffer[300]; //Received data bufferuint8_t ubr_bufferTemp[300]; //Received data bu…

K-medoids算法原理及Python实践

一、原理 K-medoids算法是一种聚类算法&#xff0c;它的原理与K-Means算法相似&#xff0c;但关键区别在于它使用数据集中的实际点&#xff08;称为medoids&#xff09;作为簇的中心点&#xff0c;而不是像K-Means那样使用簇内所有点的平均值。以下是K-medoids算法的主要原理&…

如何在算家云搭建模型Stable-Fast-3D(3D模型生成)

一、模型介绍 Stable-Fast-3D 具有 UV 展开和照明解缠的稳定快速 3D 网格重建&#xff0c;它是一种从单个图像进行快速前馈 3D 网格重建的最先进的开源模型。 二、模型搭建流程 基础环境最低要求说明&#xff1a; 环境名称版本信息1Ubuntu22.04.4 LTSCudaV12.1.105Python3.…

【项目日记】高并发内存池 ---项目介绍及组件定长池的实现

余生还长&#xff0c;你别慌&#xff0c;也别回头&#xff0c;别念旧. --- 余华 --- 1 高并发内存池简介 高并发内存池项目是实现一个高并发的内存池&#xff0c;他的原型是google的一个开源项目tcmalloc&#xff0c;tcmalloc全称Thread-Caching Malloc&#xff0c;即线程缓存…

快速排序与其例题

一、快速排序 1、简单介绍&#xff1a;快速排序&#xff08;Quick Sort&#xff09;是一种高效的排序算法&#xff0c;由计算机科学家Tony Hoare在1960年提出。它是基于分治法的排序算法&#xff0c;其基本思想和步骤如下&#xff1a; 基本概念 快速排序的核心思想是将待排序…

一种商业模式既解决引流又解决复购 你想了解一下嘛?

欢迎各位&#xff0c;我是你们的电商策略顾问&#xff0c;吴军。今天&#xff0c;我将向大家介绍一种新颖的商业模式——循环购物模式。这种模式有何独特之处&#xff1f;商家真的在进行慷慨的赠金活动吗&#xff1f;消费者在购物的同时还能获得额外收益&#xff1f;甚至可以将…

Python控制流:条件语句(if, elif, else)①

文章目录 前言1. 基本条件语句1.1 if 语句1.2 else 语句1.3 elif 语句1.4 嵌套条件语句 2. 条件表达式3. 多条件判断4. 比较运算符和逻辑运算符5. 常见错误和最佳实践5.1 常见错误5.2 最佳实践 6. 综合详细的例子&#xff1a;学生成绩管理系统6.1 类和方法Student 类 6.2 主函数…

LD/T698.45 协议解析(新)

通信架构 客户机和服务器在开始通信前&#xff0c;通信信道必须先完成预连接。预连接建立后&#xff0c;默认具有一个最低权限的应用连接&#xff0c;客户机和服务器之间可直接进行数据交换。当客户机需要得到更高权限的服务器服务时&#xff0c;客户机必须发起建立更高权限的…

浦发银行不良堆积,新任领导的无奈

撰稿|芋圆 浦发银行在2023年进行了一波董监高人员大变动&#xff0c;董事长和行长两位掌舵人双双离职&#xff0c;在其任内&#xff0c;浦发银行自2020年起的营收、利润状况和资产质量就一直难有起色。 目前&#xff0c;距新任领导班子上任已差不多过去一年之久。在这一年里&a…

Redis(面试题【速记】)

Redis简介 Redis 是一个开源(BSD 许可)内存数据结构存储用作数据库、缓存、消息代理和流引擎。Redis 提供数据结构&#xff0c;例如 字符串、散列、列表、集合、带范围查询的排序集合、位图、超日志、地理空间索引和流。Redis 内置了复制、Lua 脚本、LRU 驱逐、事务和不同级别的…

【Linux —— 线程同步 - 条件变量】

Linux —— 线程同步 - 条件变量 条件变量的概念互斥量与条件变量的关系条件变量的操作代码示例 条件变量的概念 条件变量是一种用于线程间同步的机制&#xff0c;主要用于协调线程之间的执行顺序&#xff0c;允许线程在某个条件不满足时进入等待状态&#xff0c;直到其他线程通…

【Linux I/O】万字长文带思维导图,一文彻底掌握Linux I/O:深入解析操作系统数据交互的艺术

Linux I/O Linux I/O&#xff08;输入/输出&#xff09;是操作系统与外部设备进行数据交互的过程。在Linux系统中&#xff0c;I/O操作的管理和优化对于系统性能有着至关重要的影响。本文将详细介绍Linux中的各种I/O模型&#xff0c;包括它们的工作原理、优缺点以及适用场景&am…

ImportError: DLL load failed while importing _ssl: 找不到指定的模块。

windonw cmd下的输出&#xff1a; (python3.9) PS D:\git\ImageAnalysisService\core\medical_bills> python Python 3.9.19 (main, May 6 2024, 20:12:36) [MSC v.1916 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or …

计算机基础知识总结(八股文--计算机网络、操作系统、数据库、c++、数据结构与算法)

一、操作系统 0.内存管理 01.什么是虚拟内存&#xff1f;为什么需要虚拟内存&#xff1f; 虚拟内存为程序提供比实际物理内存更大的内存空间&#xff0c;同时提高内存管理的灵活性和系统的多任务处理能力。虚拟地址空间就是进程所能看到的内存空间&#xff0c;这段空间是连续…

苍穹外卖项目DAY11

苍穹外卖项目DAY11 1、Apache ECharts 1.1、介绍 Apache ECharts是一款基于JavaScript的数据可视化图标库&#xff0c;提供直观&#xff0c;生动&#xff0c;可交互&#xff0c;可个性化定制的数据可视化图标 官网&#xff1a;Apache ECharts 1.3、入门案例 <!DOCTYPE…

LlamaIndex 实现 RAG(四)- RAG 跟踪监控

RAG 整个流程不复杂&#xff0c;集成三大部分包括文档解析并生成向量、根据查询问题查找语意相似的数据文档块、把查询问题和召回文档作为上下文的数据传给模型进行解答。大语言模型的应用开发和传统的开发方式区别很大&#xff0c;以前开发完成&#xff0c;只要逻辑正确&#…

解决IDEA 控制台中文乱码及无法输入中文

一、IDEA 控制台中文乱码&#xff1a; 问题描述&#xff1a; IntelliJ IDEA 如果不进行相关设置&#xff0c;可能会导致控制台中文乱码、配置文件中文乱码等问题。 解决方案&#xff1a; ①&#xff1a;设置字体为支持中文的字体&#xff1a; 点击菜单 File - > settings …

二分查找【算法 09】

二分查找算法详解 二分查找&#xff08;Binary Search&#xff09;是一种高效的查找算法&#xff0c;前提是数据必须是有序的。相比于线性查找&#xff0c;二分查找的时间复杂度从 O(n) 降低到了 O(log n)&#xff0c;适合处理大规模的数据查找问题。本文将详细介绍二分查找的原…

Catf1ag CTF Crypto(六)

前言 Catf1agCTF 是一个面向所有CTF&#xff08;Capture The Flag&#xff09;爱好者的综合训练平台&#xff0c;尤其适合新手学习和提升技能 。该平台由catf1ag团队打造&#xff0c;拥有超过200个原创题目&#xff0c;题目设计注重知识点的掌握&#xff0c;旨在帮助新手掌握C…