我正在读/写文件时发生了什么(在操作系统级别)?

Rom*_*man 4 java filesystems operating-system programming-languages file

假设一个程序正在读取文件F.txt,另一个程序正在同时写入该文件.

(当我考虑如果我是系统程序员时如何实现此功能)我意识到可能存在歧义:

  1. 第一个项目会看到什么?

  2. 第二个程序在哪里写新字节?(即写入"就地"vs写入新文件,然后用新文件替换旧文件)

  3. 有多少程序可以同时写入同一个文件?

    ..也许是不那么明显的事情.

所以,我的问题是:

  1. 读/写文件功能的主要策略是什么?

  2. 哪些操作系统(Windows,Linux,Mac OS等)支持哪些?

  3. 它可以依赖于某种编程语言吗?(我可以假设Java可以尝试在所有支持的操作系统上提供一些统一的行为)

vz0*_*vz0 6

从磁板/闪存单元到本地Java变量,单字节读取需要很长的路要走.这是单个字节传输的路径:

  1. 磁板/闪光灯
  2. 内部硬盘缓冲区
  3. SATA/IDE总线
  4. SATA/IDE缓冲区
  5. PCI/PCI-X总线
  6. 计算机的数据总线
  7. 计算机的RAM通过DMA
  8. 操作系统页面缓存
  9. Libc读缓冲区,又称用户空间fopen()读缓冲区
  10. 本地Java变量

出于性能原因,操作系统完成的大部分文件缓冲都保留在页面缓存中,将最近的读写文件内容存储在RAM上.

这意味着Java代码中的每个读写操作都是从本地缓冲区完成的:

FileInputStream fis = new FileInputStream("/home/vz0/F.txt");

// This byte comes from the user space buffer.
int oneByte = fis.read();
Run Code Online (Sandbox Code Playgroud)

页面通常是4KB内存的单个块.每个页面都有一些特殊的标志和属性,其中一个是"脏页面",这意味着页面有一些未写入物理媒体的修改数据.

一段时间后,当操作系统决定将脏数据刷回磁盘时,它会以相反的方向发送数据.

只要两个不同的进程将数据写入同一文件,结果就是:

  • 如果文件被锁定,则不可能.第二个进程将无法打开该文件.
  • 如果写在文件的同一区域,则未定义.
  • 如果在文件的不同区域运行,则预期.

"区域"取决于应用程序使用的内部缓冲区大小.例如,在两兆字节的文件上,两个不同的进程可能会写:

  • 一个在第一个1kB的数据上(0; 1024).
  • 另一个在最后1kB的数据(2096128; 2097152)

仅当本地缓冲区的大小为2兆字节时,才会发生缓冲区重叠和数据损坏.在Java上,您可以使用通道IO来读取文件,并对内部发生的事情进行细粒度控制.

许多事务数据库通过发出sync操作强制从本地RAM缓冲区向磁盘写入一些内容.与单个文件相关的所有数据都会被刷回磁板或闪存单元,有效地确保在电源故障时不会丢失任何数据.

最后,内存映射文件是一个内存区域,它允许用户进程直接从页面缓存读取和写入,绕过用户空间缓冲.

页面缓存系统对于多任务保护模式操作系统的性能至关重要,每个现代操作系统(Windows NT向上,Linux,MacOS,*BSD)都支持所有这些功能.