首页 > 其他分享 >操作系统中的文件操作

操作系统中的文件操作

时间:2024-10-22 15:24:28浏览次数:1  
标签:文件 操作系统 读写 程序 内存 缓冲区 操作

当我们打开电脑,通过资源管理器查看文件时,可以看到各种属性:文件的名字、大小、创建时间等等。这些文件以一种直观的方式呈现在我们眼前,它们存储在外部存储器中,如硬盘或者SSD中。然而,当我们自己写程序操作这些文件时,这种简单的可视化就不够了。那么,如何用代码和这些文件进行交互?如何实现文件的读取、写入,甚至删除呢?这就是操作系统中文件操作的内容,今天我们将带你深入了解这些背后的机制。

文件系统的差异与抽象

首先,文件系统的机制各不相同,但是只要一个能用的文件系统,文件的创建、读取、写入、删除都表现出相似的操作方式。操作系统的一个重要任务就是抹平这些底层文件系统的差异,为程序提供一种统一的接口(API)来操作文件。比如,无论是在Windows的 NTFS 还是Linux的 ext4 上,函数openreadwrite这些操作都差不多。这意味着我们在编写程序时,不需要过多关心底层是哪个文件系统,除非涉及到一些特定的文件系统专属高级功能(例如Linux的特定文件权限或Windows的NTFS数据流等)。

操作系统通过将底层文件系统的实现细节抽象化,使得应用程序可以方便地使用统一的接口进行文件操作,这样一来,我们可以专注于实现应用程序的逻辑,而不必深入了解底层的文件系统的复杂性。举个例子,NTFS和ext4文件系统虽然在实现上有很多不同,但程序员可以通过相同的openreadwrite函数完成同样的文件操作。这种抽象化也使得程序的跨平台能力大大增强,让程序员可以编写一次代码,便可以在不同平台上运行。

而高级语言通常又提供了自己的文件操作库。操作系统抹平了文件系统的差异,而高级语言又抹平了操作系统的差异,使得编程更加简单。

文件操作的基本流程:打开、读写和关闭

在编程中操作文件,通常的流程是:打开文件读写文件关闭文件。当我们打开一个文件时,往往是通过文件的名字(如"myfolder/myfile.txt")来找到它,然后系统会返回一个文件描述符(File Descriptor, FD)(在 Windows 下叫做文件句柄 Handle,后文仍用 FD 来称呼)。这个描述符实际上是一个整数,它唯一地代表了当前程序中的当前文件,之后对文件的所有操作都要通过这个描述符来进行。

文件的读写操作依赖于这个描述符和一个操作缓冲区(Buffer)。当你准备用 buffer 数组装下文件内容,read(fd, buffer, size)函数将从文件中读取数据;如果要将 buffer 数组写入文件,而write(fd, buffer, size)函数则会将缓冲区的数据写入文件。完成操作后,我们需要通过close(fd)来关闭文件,释放系统资源。

这意味着在整个文件操作过程中,打开、读写、关闭是密不可分的步骤。打开文件相当于向操作系统申请访问权限,并获取一个描述符,读写是通过这个描述符来具体实现数据的传递,最后通过关闭文件来释放占用的资源,确保系统的资源得以回收。每个步骤都有其必要性和作用,缺一不可。

文件读写模式和指针

打开文件时,我们可以在系统调用参数里选择不同的模式,常见的模式包括:

  • r表示只读,文件不存在则失败
  • w表示写入,清空原内容,文件不存在则创建
  • a表示追加写入,不清空原内容,且文件指针默认在文件末,文件不存在则创建
  • r+/w+/a+表示读写,写读,追加。可同时读写性质参考r,w 和 a
  • rb/wb/ab表示二进制读/写,在以上模式后加上 b,表明处理的是二进制文件;与之对应的不加 b 的默认模式(即文本读/写),操作系统进行回车处理等操作。读取一个纯文本文件时,可以通过文本模式来简化换行符的处理,而在读取图片、音频等二进制文件时,则需要使用二进制模式以保证数据的原始格式不被破坏。

在一般情况下,读写文件的时候有一个隐含的文件指针(File Pointer),它用于标记文件中当前读写的位置。例如,r 和 w 的指针默认在文件开头,随着你进行顺序读写自动向后移动。所以如果你需要正常读全文或顺序写,指针自动移动让你感知不到其存在。

指针的移动也可以手动完成,通过lseek(fd, offset, whence)函数,你可以将指针移动到文件的任意位置。这样就可以随机地访问文件中的内容。这种指针的存在和操作使得文件的访问更加灵活,尤其是在处理大文件时。通过随机访问的方式,可以跳过不必要的数据读取,直接访问感兴趣的部分。

缓冲区与文件读写的关系

逻辑上,open将文件信息加载到内存,readwrite会在主存和外存间传送数据。但实际上,为了效率考虑,调用open函数只会加载一些基本信息,并不会立即将文件的内容加载到内存中,实际上文件的读取是在调用read时逐步完成的。

具体来说,而第一次读取文件时,操作系统会将文件中的一部分数据读入缓冲区(通常是一次性读取比请求的数据更多的数据),这样当程序继续需要读取时,可以直接从内存中取出而无需每次都访问磁盘。

类似地,调用write时,数据并不会立即写回磁盘,而是会先存入内存中的缓冲区(Buffer),稍后再统一写回。这种机制提高了文件操作的效率,而在close时,所有缓冲区数据会正确写回。我们也可以通过调用fsync(fd)等系统调用手动控制缓冲区的刷新,以确保数据及时保存。

缓冲区的存在可以显著提升文件的读写效率,尤其是在频繁的小规模读写操作中。不过,缓冲区的使用也带来了风险,比如在程序异常终止时,缓冲区中的数据可能尚未写回磁盘,从而造成数据丢失。因此,程序员需要谨慎处理,确保在合适的时机刷新缓冲区,并最终调用close来让操作系统确认写回数据保证数据的一致性和完整性。即使如此,断电等也可能导致不一致,你可以手动控制缓冲区的写回,在效率和一致性上自行取舍。

系统如何处理文件的打开、读写和关闭

逻辑上,当我们调用open函数时,操作系统会在磁盘上查找文件,并将文件的基本信息(如大小、权限等)加载到内存中。在执行read操作时,操作系统会将文件的内容逐块复制到内存缓冲区中,而执行write操作则是反向地将缓冲区的数据写入到磁盘。在调用close时,操作系统会确保将所有未写入的数据写回磁盘,并释放相关资源。

多个程序可以同时打开同一个文件,但它们各自的描述符和指针是独立的。系统中维护了一个全局打开文件表(Global Open File Table),其中每个文件只会在首次被打开时加载基本信息到全局表中。当多个程序打开同一个文件时,系统会在全局打开文件表中只保留一份条目,并且与所有打开该文件的程序形成一对多的映射。这样做的好处是减少了文件多次打开的开销,避免了重复读取文件信息。只有当所有打开该文件的程序都调用close关闭文件时,该文件的条目才会从全局打开文件表中移除。

这种设计带来了很多优势,特别是在需要频繁访问同一个文件时。全局打开文件表的存在减少了重复的I/O操作,使得程序对文件的访问更加高效。多个程序共享一个全局条目,而操作系统则负责维护每个程序自己的文件描述符和指针,这样既提高了资源利用率,又确保了不同程序之间的操作不会互相干扰。

多程序对文件的并发访问

如前所述,系统中维护了全局的打开文件表,这使得多个程序可以共享同一个文件的基础信息,而各自拥有独立的文件描述符和指针。这样,尽管不同程序可以共享文件,但它们的读写行为互不干扰,从而提高了文件访问的效率。

多个程序可以同时打开并读取同一个文件,但每个程序都有各自独立的文件描述符和指针。在操作系统层面,open/read/write本身没有限制并发的读写,但由于缓冲区的存在,多个程序同时读写同一个文件时,可能会导致数据的不一致。这种情况下需要程序员自己设计机制(如锁)来防止数据冲突或损坏。

在并发写入时,问题会变得更加复杂。操作系统也不会主动防止两个程序同时写入同一个文件,因此可能会产生竞态条件(Race Condition)。这意味着如果多个程序同时对文件进行写操作,数据的正确性将无法保证,可能会出现覆盖、交叉等错误。因此,程序员在并发环境下进行文件写操作时,需自行设计合理机制。

内存映射文件:mmap与mapviewoffile

除了传统的open/read/write方式,还有一种机制是通过内存映射(Memory Mapping)来操作文件。在Linux中可以通过mmap函数,在Windows中可以通过MapViewOfFile函数实现。这种方式将文件的内容直接映射到进程的内存地址空间中,因此程序可以通过直接访问内存的方式来读取或写入文件。这种机制特别适合于对大文件的随机读写操作,因为它避免了多次系统调用的开销,且在多程序之间共享文件时,可以使得对文件的修改立即对所有映射的进程可见。

内存映射文件的优点在于它简化了对文件的读写操作,特别是在多进程间共享文件的场景下,但它的缺点是对内存的消耗较大,并且不适合对小片段数据进行频繁的读写操作。通过内存映射,程序可以将文件的某一部分或全部映射到内存中,这样对文件内容的访问就如同访问普通内存一样简单快速。此外,在多个进程共享同一个文件时,缓存和回写由 OS 控制,内存映射可以确保一个进程的修改对其他进程立即可见,从而实现高效的数据共享。

不过,内存映射也有其局限性。首先,它对系统内存的消耗较大,尤其是在映射大文件时,可能会占用大量的虚拟内存。其次,对于频繁的小规模写操作,内存映射并不是最佳选择,因为它可能会导致内存和磁盘之间的频繁同步,进而降低效率。因此,程序员需要根据具体的应用场景选择合适的文件操作方式,以达到最佳的性能效果。

标签:文件,操作系统,读写,程序,内存,缓冲区,操作
From: https://www.cnblogs.com/ofnoname/p/18492858

相关文章

  • 磁力天堂视频下载时DLL文件不见了?这里提供解决方案
    当您在使用磁力天堂或者任何其他软件时遇到丢失DLL文件的提示,这通常意味着某个动态链接库文件(.dll)缺失或损坏,这个文件是Windows操作系统的一部分,也是很多应用程序运行所必需的。以下是一些可能的解决方法:重新安装软件:尝试卸载磁力天堂软件,然后从官方网站或可信来源重新下载......
  • 《斗罗大陆:猎魂世界》启动提示DLL文件丢失?简单几步轻松修复
    当您在启动《斗罗大陆:猎魂世界》游戏时遇到丢失DLL文件的错误提示,这通常意味着游戏运行所必需的一个或多个动态链接库文件缺失或损坏。以下是一些常见的解决方法:重新安装游戏:卸载游戏后,彻底删除游戏文件夹中的残留文件。从官方渠道下载最新版本的游戏安装程序,并进行全新安......
  • iOS开发 钥匙串中的证书无法导出(个人信息).p12文件
    解决方法1、直接把需要导出.p12的.cer文件复制粘贴到[钥匙串访问][登录]的分类下的文件中。然后就可以导出.p12文件了就是这么简单2、紧急提醒:如果上面的办法不行那么先执行下面的事情重新生成.cer文件再重新做1上面的事情。可能是Mac系统缺少AppleWWDRCA.cer证书。解决......
  • P4247 [清华集训2012]序列操作
    题目描述有一个长度为\(n\)的序列,有三个操作:Iabc表示将\([a,b]\)这一段区间的元素集体增加\(c\);Rab表示将\([a,b]\)区间内所有元素变成相反数;Qabc表示询问\([a,b]\)这一段区间中选择\(c\)个数相乘的所有方案的和\(\mod19940417\)的值。对于100%的数据,\(n\leq500......
  • ArgoWorkflow教程(七)---高效的步骤间文件共享策略
    之前我们分析了使用artifact实现步骤间文件共享,今天分享一下如何使用PVC实现高效的步骤间文件共享。1.概述之前在artifact篇我们演示了如何使用artifact实现步骤间文件传递,今天介绍一种更为简单的文件传递方式:PVC共享。artifact毕竟是借助S3实现中转,效率上肯定是......
  • Day 9 文件基本处理 + 相(绝)对路径 + 文件打开方式 + 文件拷贝
    目录0昨日回顾字符编码1基本的文件处理1.1文件是什么?1.2如何使用文件1.3读取关闭1.4写2绝对路径与相对路径2.1绝对路径2.2相对路径3文件打开的三种方式3.1mode='r',只读3.2mode='w',只写,清空文件再写入3.3mode='a',追加,在后面追加写入文件3.4mode='rt',打开的......
  • Java项目中引入达梦数据库依赖驱动并且操作链接数据库
    本人详解作者:王文峰,参加过CSDN2020年度博客之星,《Java王大师王天师》公众号:JAVA开发王大师,专注于天道酬勤的Java开发问题中国国学、传统文化和代码爱好者的程序人生,期待你的关注和支持!本人外号:神秘小峯山峯转载说明:务必注明来源(注明:作者:王文峰哦)Java项目中引......
  • 蓝桥杯基本操作和运算
    文章目录1.基本运算2.循环--进制转换/最大公约数2.1进制转换2.2求解最大公约数3.数组与字符串4.常用的API5.快速读写模版蓝桥杯基本操作和运算10-22号正式开始准备蓝桥杯的比赛,准备参加这个大学B组的Java的赛项1.基本运算首先就是基本的输入输出:system.out.pr......
  • 【Markdown速成】半小时入门Markdown(后缀.md文件详解)
    目录一.认识MarkdownMarkdownTypora二.Typora设置三.Markdown语法1.标题2.正文2.1分割线2.2删除线2.3下划线2.4斜体2.5粗体2.6斜粗体2.7高亮 2.8字体属性(大小及颜色)2.9对齐方式2.10引用3.列表有序列表无序列表 4.超链接5.表格6.代码一.认识Markdown......
  • 找到占用磁盘最多的文件或目录,可以使用du和sort
    想要找到占用磁盘最多的文件或目录,可以使用du和sort命令: du-h/path/to/directory|sort-rh|head-n10其中:du-h/path/to/directory用于计算指定目录下的所有文件和子目录的大小,并以人类可读的格式显示。sort-rh用于按磁盘使用情况的大小(逆序)对输出进行排序。h......