假设一个程序正在读取文件F.txt,而另一个程序正在同时写入该文件。
(当我考虑如果我是一名系统程序员时该如何实现此功能)我意识到在以下方面可能存在歧义:

  • 第一个程序会看到什么?
  • 第二个程序在哪里写新字节? (即写“就地”与写入新文件,然后用新文件替换旧文件)
  • 有多少个程序可以同时写入同一文件?
    ..也许不是那么明显。

  • 因此,我的问题是:
  • 读取/写入文件功能的主要策略是什么?
  • 哪个操作系统(Windows,Linux,Mac OS等)支持哪些功能?
  • 可以依赖某些编程语言吗? (我可以假设Java可以尝试在所有受支持的OS上提供一些统一的行为)
  • 最佳答案

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

  • 磁性板/闪光灯
  • 内部硬盘缓冲区
  • SATA / IDE总线
  • SATA / IDE缓冲区
  • PCI / PCI-X总线
  • 计算机的数据总线
  • 通过DMA的计算机RAM
  • 操作系统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内存的单个块。每个页面都有一些特殊的标志和属性,其中一个是“脏页面”,这意味着该页面具有一些未写入物理介质的修改数据。

    一段时间后,当OS决定将脏数据刷新回磁盘时,它将以与原始数据相反的方向发送数据。

    每当两个不同的进程将数据写入同一文件时,结果是:
  • 如果文件已锁定,则不可能。第二个进程将无法打开文件。
  • 如果覆盖文件的相同区域,则未定义。
  • 如果在文件的不同区域上操作,则应为。

  • “区域”取决于应用程序使用的内部缓冲区大小。例如,在一个2 MB的文件上,两个不同的进程可能会写:
  • 数据的前1kB(0; 1024)上的一个。
  • 数据的最后1kB中的另一个(2096128; 2097152)

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

    许多事务数据库通过发出 sync operation来强制将某些本地RAM缓冲区的写操作写回到磁盘上。与单个文件有关的所有数据都将刷新回磁板或闪存单元,从而有效地确保在断电时不会丢失任何数据。

    最后,memory mapped file是一个内存区域,它使用户进程可以绕过用户空间缓冲而直接从页面高速缓存中读写数据。

    Page Cache系统对于多任务protected mode OS的性能至关重要,每个现代操作系统(Windows NT或更高版本,Linux,MacOS和* BSD)都支持所有这些功能。

    09-25 17:43