3. 深入探究文件 IO

3. 深入探究文件 IO

  • 1. Linux 系统如何管理文件
    • 1.1 静态文件与inode
    • 1.2 文件打开时的状态
  • 2. 返回错误处理与errno
    • 2.1 strerror 函数
    • 2.2 perror 函数
  • 3. 空洞文件
  • 4. O_APPEND 和 O_TRUNC
  • 5. 多次打开同一个文件
  • 6. 复制文件描述符
    • 6.1 dup
    • 6.2 dup2
  • 7. 文件共享
    • 7.1 同一个进程中多次调用 open 函数打开同一个文件
    • 7.2 不同进程分别使用 open 函数打开同一个文件
    • 7.3 同一个进程中对文件描述符进行复制
  • 8. 原子操作与竞争冒险
    • 8.1 O_APPEND
    • 8.2 pread 和 pwrite
    • 8.3 创建一个文件
  • 9. fcntl 和 ioctl
    • 9.1 fcntl
    • 9.2 ioctl
  • 10. 截断文件

1. Linux 系统如何管理文件

1.1 静态文件与inode

文件没有被打开的情况下一般都是存放在磁盘中的,并且以一种固定的形式进行存放,这时称为静态文件。文件存储在硬盘上,硬盘的最小存储单元叫做扇区,每个扇区大小是 512 字节, 相当于 0.5k。操作系统读取硬盘的时候不会一个个扇区的读取,而是一次读取多个扇区,也就是一个块, 通常是 4k,也就是 8 个扇区。将磁盘进行分区格式化的时候会分为数据区和 inode 区,inode 区存放 inode 表,该表中存放着 inode 节点,每个节点都是一个结构体,包含着对应文件的属性信息。所以查找文件时,先根据文件名找到对应的 inode 编号,然后找到对应的表,最后查找相关信息,读取数据。
在这里插入图片描述

1.2 文件打开时的状态

调用 open 打开文件的时候,内核会申请一段内存,将数据读取到内存中进行管理,也就是动态文件。对动态文件进行读写操作时,和静态文件不会同步,数据的同步由内核完成,内核会在之后将内存这份动态文件同步到磁盘中。静态文件有多个块,一个块有多个扇区,一个扇区有多个字节,所以对静态文件操作时需要反复读写块,而内存可以直接一个字节一个字节的改动,所以速率较快。
在 Linux 系统中,内核会有一个专门的数据结构管理一个进程,叫做 PCB。该结构体中有一个指针指向了文件描述符表,而文件描述符表中的每一个元素对应文件表,文件表存放着文件的相关信息。

2. 返回错误处理与errno

当发生错误时,操作系统会将错误对应的编号赋值给 errno 变量,每个进程都有一个自己的 errno 全局变量

2.1 strerror 函数

#include <string.h>
char *strerror(int errnum);	// 参数就是对应的errno,返回对应错误编号的字符串描述信息

2.2 perror 函数

#include <stdio.h>
void perror(const char *s);	// 参数可以传递自己想要的信息,然后错误描述就会打印在s之后

3. 空洞文件

lseek 函数还允许文件偏移量超出文件长度,也就是文件末尾还可以向后偏移。比如一个文件只有 4096k,此时在文件头部向后偏移 6000 字节,然后在这里写入数据,那么 4096 ~ 6000 这部分就是空洞。文件空洞部分不会占用任何物理空间,但是空洞文件形成时,逻辑上该文件的大小是包含了空洞部分的大小的。 文件空洞在多线程共同操作文件时有很大作用,可以将文件分段,不同线程在不同空洞部分写入数据。

4. O_APPEND 和 O_TRUNC

O_TRUNC 会将文件原本的内容清除,然后再写入数据,而O_APPEND 是在文件末尾写入数据。

5. 多次打开同一个文件

一个进程内多次打开一个文件,那么会得到多个不同的文件描述符,同理在关闭的时候需要依次关闭对应的文件描述符。而且在内存中不会存在多份动态文件,不同文件描述符对应的读写位置偏移量是相互独立的。因为位置偏移量是相互独立的,所以对不同的文件描述符读写时,是分别进行读写。使用 open 函数打开文件时,默认是覆盖式写入,也就是说当分别进行写入操作时,后续写入数据时会先将文件清空再写入。 不同的文件描述符就对应不同的文件表,而位置偏移量就保存在文件表中,但是文件表中的 inode 指针指向的都是同一个 inode。
同样,多个不同的进程打开同一个文件,在内存中也只是维护一份动态文件,多个进程间共享,有各自独立的文件读写位置偏移量。当文件的引用计数为 0 时,系统会自动关闭文件。

6. 复制文件描述符

在 Linux 系统中,open 得到的文件描述符可以进行复制,新的文件描述符也可以对旧文件描述符指向的文件进行操作,拥有相同的权限。但是新的文件描述符和旧的文件描述符指向的文件表是同一个

6.1 dup

#include <unistd.h>
int dup(int oldfd);	// 成功返回由系统分配的新的文件描述符,失败返回-1
#include <stdio.h>
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
using namespace std;int main()
{int fd1=open("./text.txt",O_CREAT|O_RDWR,0777);int fd2=dup(fd1);write(fd1,"hello ",6);write(fd2,"world!",6);return 0;
}

在这里插入图片描述

6.2 dup2

#include <unistd.h>
int dup2(int oldfd,int newfd);	// 成功返回newfd,失败返回-1
int main()
{int fd1=open("./text.txt",O_CREAT|O_RDWR,0777);int fd2=dup2(fd1,9);	// 可以指定文件描述符write(fd1,"hello ",6);write(fd2,"world!",6);cout << fd2<<endl;return 0;
}

在这里插入图片描述

7. 文件共享

7.1 同一个进程中多次调用 open 函数打开同一个文件

在这里插入图片描述
会得到同一个文件的不同文件描述符,并且多个文件描述符对应多个不同的文件表,所有的文件表指向同一个 inode 节点

7.2 不同进程分别使用 open 函数打开同一个文件

在这里插入图片描述

7.3 同一个进程中对文件描述符进行复制

在这里插入图片描述

8. 原子操作与竞争冒险

当两个独立的进程对同一个文件进行操作时,因为此时文件是共享的,如果当一个进程的操作未完成时,另一个进程就对文件进行操作就会发生竞争冒险。所以就有了原子操作。原子操作是指一个任务要么不做,要么做完。
O_APPEND、pread() 和 pwrite()、创建文件都是可以实验原子操作的。

8.1 O_APPEND

两个进程都向文件中写入数据,后一个进程会覆盖前一个进程写入的内容,就需要使用该标志

8.2 pread 和 pwrite

这两个函数可传入一个位置偏移量,用于指定文件当前读写的位置偏移量。但是不更新文件表中当前位置偏移量,就是说在当前位置 0 调用这两个函数时如果设置 offset 为1024,然后再调用 lseek 获取当前位置,发现依旧是 0

#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);

8.3 创建一个文件

如果两个进程都会创建同一个文件,就需要使用 O_EXCL,如果要打开的文件已经存在,就 open 失败,如果不存在,就创建这个文件

9. fcntl 和 ioctl

9.1 fcntl

fcntl 函数可以对一个已经打开的文件描述符执行一系列控制操作

#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd,.../* arg */);
/* cmd:操作命令,表示我们将对fd进行什么操作* F_DUPFD  或  F_DUPFD_CLOEXEC	:复制文件描述符* F_GETFD  或  F_SETFD			:获取/设置文件描述符标志* F_GETFL  或  F_SETFL			:获取/设置文件状态标志* F_GETOWN 或  F_SETOWN		:获取/设置异步IO所有权* F_GETLK  或  F_SETLK			:获取/设置记录锁* /
// 第三个参数根据cmd来传入对应的实参
// 返回值是失败返回-1,成功根据cmd有不同的返回值

复制文件描述符

int main()
{int fd1=open("./test.txt",O_CREAT|O_RDONLY,0777);int fd2=fcntl(fd1,F_DUPFD,0);// 如果传入的第三个参数已经被使用,就返回一个比0大的可使用的文件描述符,否则就返回第三个参数cout << fd2 << endl;return 0;
}

获取/设置文件状态标志

int main()
{int fd1=open("./test.txt",O_CREAT|O_RDWR,0777);int flag=fcntl(fd,F_GETFL);int fd2=fcntl(fd1,F_SETFL,flag|O_APPEND);cout << fd2 << endl;return 0;
}

F_GETFL 成功时返回状态标志,F_SETFL 的第三个参数表示需要设置的状态标志。但是文件权限标志(O_RDONLY、 O_WRONLY、 O_RDWR)以及文件创建标志(O_CREAT、O_EXCL、 O_NOCTTY、 O_TRUNC)不能被设置,只有 O_APPEND、 O_ASYNC、O_DIRECT、 O_NOATIME 以及 O_NONBLOCK 这些标志可以被修改

9.2 ioctl

可以认为是文件 IO 操作的杂物箱,一般用于操作特殊文件或硬件外设,比如获取 LCD 相关信息等,这里只是介绍以下

#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request,...);
// request 表示向文件描述符请求相应的操作,第三个可变参数根据request设置

10. 截断文件

使用系统调用 truncate() 或 ftruncate()可将普通文件截断为指定字节长度

#include <unistd.h>
#include <sys/types.h>
int truncate(const char *path, off_t length);
int ftruncate(int fd, off_t length);// 使用前必须open,并且有可写权限

先创建两个文件,这时文件大小都是0字节
在这里插入图片描述
向文件中插入数据,改变文件大小

int main()
{int fd1=open("./file1",O_RDWR);char buffer1[4096]={0};char buffer2[2048]={0};write(fd1,buffer1,sizeof(buffer1));write(open("./file2",O_RDWR),buffer2,sizeof(buffer2));return 0;
}

在这里插入图片描述

截断文件,发现文件大小改变了

int main()
{int fd1=open("./file1",O_RDWR);char buffer1[4096]={0};char buffer2[2048]={0};write(fd1,buffer1,sizeof(buffer1));write(open("./file2",O_RDWR),buffer2,sizeof(buffer2));ftruncate(fd1,1024);truncate("./file2",2048);return 0;
}

在这里插入图片描述

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

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

相关文章

Python 测试框架 Pytest 的入门

简介 pytest 是一个功能强大而易于使用的 Python 测试框架。它提供了简单的语法和灵活的功能&#xff0c;用于编写和组织测试代码。 1、简单易用&#xff1a;pytest 的语法简洁明了&#xff0c;使得编写测试用例更加直观和易于理解。它使用 assert 语句来验证预期结果&#x…

从0到0.01入门 Webpack| 005.精选 Webpack面试题

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

【蓝桥杯】刷题

刷题网站 记录总结刷题过程中遇到的一些问题 1、最大公约数与最小公倍数 a,bmap(int,input().split())sa*bwhile a%b:a,bb,a%bprint(b,s//b)2.迭代法求平方根(题号1021) #include<stdio.h> #include<math.h> int main() {double x11.0,x2;int a;scanf("%d&…

【RTP】RTPSenderAudio::SendAudio

RTPSenderAudio 可以将一个opus帧封装为rtp包进行发送,以下是其过程:RTPSenderAudio::SendAudio :只需要提供payload部分 创建RtpPacketToSend 并写入各个部分 填充payload部分 sender 本身分配全session唯一的twcc序号 if (!rtp_sender_->

时间序列预测实战(十九)魔改Informer模型进行滚动长期预测(科研版本)

论文地址->Informer论文地址PDF点击即可阅读 代码地址-> 论文官方代码地址点击即可跳转下载GIthub链接 个人魔改版本地址-> 文章末尾 一、本文介绍 在之前的文章中我们已经讲过Informer模型了&#xff0c;但是呢官方的预测功能开发的很简陋只能设定固定长度去预测未…

Git控制指令

git status查看当前本地分支的修改状态 git diff 文件路径 查看具体文件的修改内容 git log打印用户信息 git remote -v查看远程地址 git checkout -- *还原被删除的文件 git rm -r --force .删除本地所有文件 git commit -m "Remove all files from repositor…

Xilinx Zynq-7000系列FPGA实现视频拼接显示,提供两套工程源码和技术支持

目录 1、前言免责声明 2、相关方案推荐FPGA图像处理方案FPGA视频拼接叠加融合方案推荐 3、设计思路详解Video Mixer介绍 4、工程代码1&#xff1a;2路视频拼接 HDMI 输出PL 端 FPGA 逻辑设计PS 端 SDK 软件设计 5、工程代码2&#xff1a;4路视频拼接 HDMI 输出PL 端 FPGA 逻辑设…

BER编码规则

文章目录 一、BER 编码规则介绍二、BER编码数据组成三、Identifier octets Type1. tag class 分类2. p/c 分类3 tag type4. ASN.1的原始数据类型&#xff08;TAG&#xff09; 四、 Length octets1、定长的短格式2、不定长格式3、保留格式 五、Contents octets六 、BER编码番外 …

【数据库篇】关系模式的表示——(2)规范化

范式&#xff1a;范式是符合某一种级别的关系模式的集合 规范化&#xff1a;是指一个低一级的范式的关系模式&#xff0c;通过模式的分解转换为若干个高一级范式的关系模式的集合。 1NF 每个分量必须是不可分开的数据项&#xff0c;满足这个条件的关系模式就是1NF。 2NF 若…

信息检索指标直接优化的通用近似框架

1、直接优化信息检索指标的背景 1.1、存在问题 直接优化信息检索的指标是信息检索的一大方向。主要包含两类方法&#xff0c;一类是将IR指标作为上界进行优化&#xff1b;另一类是使用平滑函数近似表示IR指标进行优化。 直接优化IR指标方式很自然&#xff0c;但没有提供理论…

【Unity】接入MAX聚合广告SDK Applovin + GoogleAdmob

版本&#xff1a; Unity&#xff1a;2019.4.35f1gradle plugin: 4.2.0 &#xff08;实际要7.0 对应build_tools:34.0.0) gradle: 6.7.1 &#xff08;实际要7.0 对应build_tools:34.0.0) jdk: 1.8.0_241build_tools: 34.0.0 ndk: android-ndk-r19 文档&#xff1a; 6.0.1(Andro…

基于springboot+Web实现社区医院管理服务系统项目【项目源码+论文说明】计算机毕业设计

基于springbootWeb实现社区医院管理服务系统演示 摘要 在Internet高速发展的今天&#xff0c;我们生活的各个领域都涉及到计算机的应用&#xff0c;其中包括社区医院管理服务系统的网络应用&#xff0c;在外国线上管理系统已经是很普遍的方式&#xff0c;不过国内的管理系统可…

Redis分片集群

文章目录 Redis分片集群搭建分片集群散列插槽插槽原理小结 集群伸缩需求分析创建新的redis实例添加新节点到redis转移插槽 故障转移自动故障转移手动故障转移 RedisTemplate访问分片集群 Redis分片集群 搭建分片集群 主从和哨兵可以解决高可用、高并发读的问题。但是依然有两…

KT404A远程更换语音芯片方案支持OTA 4G 蓝牙 wifi 物联网

目录 一、简介 2.1 芯片的硬件 2.2 测试的方法 一、简介 KT404A远程更换语音芯片方案支持OTA 4G 蓝牙 wifi 物联网 在线下载的mp3语音芯片ic&#xff0c;通过KT404A的uart直接更换内部的语音文件&#xff0c;从而实现动态的更新语音内容 物联网大潮的来袭。随着智能家居、…

3.2 CPU的自动化

CPU的自动化 改造1-使用2进制导线改造2根据整体流程开始改造指令分析指令MOV_A的开关2进制表格手动时钟gif自动时钟gif 根据之前的CPU内部结构改造,制造一个cpu控制单元 改造一 之前的CPU全由手动开关自己控制,极度繁琐,而开关能跟二进制一一对应, 开:1, 关:0图1是之前的, …

【网络】DNS协议、ICMP协议、NAT技术

DNS协议、ICMP协议、NAT技术 一、DNS协议1、产生背景2、域名简介3、域名解析的工作流程4、使用dig工具分析DNS过程 二、ICMP协议1、ICMP介绍2、ICMP协议格式3、ping命令4、traceroute命令 三、NAT技术1、NAT技术背景2、NAT IP转换过程3、地址转换表4、NAPT技术5、重新理解路由器…

2023年亚太杯数学建模A题——深度学习苹果图像识别(思路+模型+代码+成品)

Image Recognition for Fruit-Picking Robots 水果采摘机器人的图像识别功能 问题 1&#xff1a;计数苹果 根据附件 1 中提供的可收获苹果的图像数据集&#xff0c;提取图像特征&#xff0c;建立数学模型&#xff0c;计算每幅图像中的苹果数量&#xff0c;并绘制附件 1 中所有…

佳易王各行业收银管理系统软件,企业ERP管理软件,企业或个体定制开发软件以及软件教程资源下载总目录,持续更新,可关注收藏查阅

系统简介 1、佳易王软件功能实用、操作简单、软件绿色免安装&#xff0c;解压即可使用&#xff0c;软件已经内置数据库&#xff0c;不需再安装其他数据库文件。 2、佳易王软件&#xff0c;已经形成系列&#xff0c;上百款管理系统软件涵盖多个行业。 3、已为多个企业个体定制…

在 Go 中使用 Protocol Buffers

各位准备好了吗&#xff01;这一次&#xff0c;我们将深入探讨 Protocol Buffers&#xff08;protobuf&#xff09;及其在数据序列化中的超能力所在。 介绍 Protocol Buffers&#xff0c;也被称为 protobuf&#xff0c;是由谷歌开发的一种语言无关的二进制序列化格式。其主要…

MySQL日期函数sysdate()与now()的区别,获取当前时间,日期相关函数

select sleep(2) as datetime union all select sysdate() -- sysdate() 返回的时间是当前的系统时间&#xff0c;而 now() 返回的是当前的会话时间。 union all select now() -- 等价于 localtime,localtime(),localtimestamp,localtimestamp(),current_timestamp,curre…