当我读/写文件时(在操作系统级别)发生了啥?
Posted
技术标签:
【中文标题】当我读/写文件时(在操作系统级别)发生了啥?【英文标题】:What's going on (in the OS level) when I'm reading/writing a file?当我读/写文件时(在操作系统级别)发生了什么? 【发布时间】:2011-06-19 15:43:05 【问题描述】:假设一个程序正在读取文件 F.txt,而另一个程序正在同时写入该文件。
(当我考虑如果我是系统程序员我将如何实现此功能时)我意识到可能存在歧义:
第一个程序会看到什么?
第二个程序在哪里写入新字节? (即“就地”写入与写入新文件,然后用新文件替换旧文件)
有多少程序可以同时写入同一个文件?
.. 或许有些不那么明显。
所以,我的问题是:
读写文件功能的主要策略是什么?
它们在哪些操作系统(Windows、Linux、Mac OS 等)中受支持?
它可以依赖于某些编程语言吗? (我可以假设 Java 可以尝试在所有支持的操作系统上提供一些统一的行为)
【问题讨论】:
【参考方案1】:单字节读取有很长的路要走,从磁板/闪存单元到您的本地 Java 变量。这是单个字节经过的路径:
-
磁板/闪速电池
内部硬盘缓冲区
SATA/IDE 总线
SATA/IDE 缓冲区
PCI/PCI-X 总线
计算机的数据总线
计算机的 RAM 通过DMA
操作系统Page-cache
Libc 读取缓冲区,也就是用户空间
fopen()
读取缓冲区
本地 Java 变量
出于性能原因,操作系统完成的大部分文件缓冲都保存在页面缓存中,将最近读取和写入的文件内容存储在 RAM 上。
这意味着 Java 代码的每次读写操作都是在本地缓冲区中完成的:
FileInputStream fis = new FileInputStream("/home/vz0/F.txt");
// This byte comes from the user space buffer.
int oneByte = fis.read();
一个页面通常是一个 4KB 的内存块。每个页面都有一些特殊的标志和属性,其中之一是“脏页”,这意味着该页面有一些未写入物理媒体的修改数据。
一段时间后,当操作系统决定将脏数据刷新回磁盘时,它会将数据发送到与原来相反的方向。
每当两个不同的进程将数据写入同一个文件时,产生的行为是:
不可能,如果文件被锁定。第二个进程将无法打开文件。 未定义,如果写入文件的同一区域。 预期,如果对文件的不同区域进行操作。“区域”取决于应用程序使用的内部缓冲区大小。例如,在一个 2 MB 的文件上,两个不同的进程可能会写入:
前 1kB 数据中的一个 (0; 1024)。 另一个关于最后 1kB 的数据(2096128;2097152)仅当本地缓冲区大小为 2 MB 时,才会发生缓冲区重叠和数据损坏。在 Java 上,您可以使用 Channel IO 来读取文件,并对里面发生的事情进行细粒度控制。
许多事务性数据库通过发出sync
operation 来强制将本地 RAM 缓冲区中的一些写入写回磁盘。与单个文件相关的所有数据都被刷新回磁板或闪存单元,有效地确保在断电时不会丢失任何数据。
最后,memory mapped file 是一个内存区域,它允许用户进程直接从页面缓存读取和写入页面缓存,绕过用户空间缓冲。
页面缓存系统对于protected mode 多任务操作系统的性能至关重要,每个现代操作系统(Windows NT 及以上、Linux、MacOS、*BSD)都支持所有这些功能。
【讨论】:
【参考方案2】:策略可以和文件系统一样多。通常,SO 侧重于通过在文件与光盘同步之前缓存文件来避免 I/O 操作。从缓冲区读取将看到以前保存的数据。所以在软硬件之间是一层缓冲(比如mysql MyISAM引擎就用这个层很多)
JVM 在关闭文件或程序调用 fsync() 之类的方法时将文件描述符缓冲区同步到磁盘,但是当缓冲区超过定义的阈值时,也可以通过 SO 同步。在 JVM 中,这当然是在所有支持的操作系统上统一的。
【讨论】:
【参考方案3】:http://ezinearticles.com/?How-an-Operating-Systems-File-System-Works&id=980216
【讨论】:
以上是关于当我读/写文件时(在操作系统级别)发生了啥?的主要内容,如果未能解决你的问题,请参考以下文章
无法打开相机意图。 queryIntentActivities 在 api 级别 30 返回一个空列表,但在级别 29 中找到结果。发生了啥变化?