Java 写入文件到磁盘会经历哪些过程?

Sherwin.Wei Lv7

Java 写入文件到磁盘会经历哪些过程?

重点回答

这个问题虽然主要考察对操作系统缓存、文件系统和磁盘的理解,不过最好在开头再提一下 Java 中的操作,表现出从应用层到底层 DMA 的全方位理解。

第一步:写入文件缓冲区

为了提高效率,写入文件都会利用缓冲区,即当写入数据时,数据先被存储到缓冲区,而不是直接写入磁盘。缓冲区的目的是减少磁盘 IO 操作的次数,提高性能。

一般用的是 BufferedOutputStreamBufferedWriter 类。

第二步:write 系统调用(不是 Java 中的 write)

当缓冲区写满了,或者 Java 程序主动调用 flush()close() 时,会触发相应的系统调用(write()),将数据从用户态传递到内核态。

操作系统接收到系统调用后,会将数据写入内核缓冲区(通常称为Page Cache)。这时,数据还在内存中,并未立即写入磁盘。

第三步:DMA 拷贝

操作系统中的 I/O 调度器决定实际的写入顺序,等到调度后,数据从内核缓冲区通过 DMA(Direct Memory Access)或中断机制传递到磁盘控制器的硬件缓冲区

第四步:写入磁盘

磁盘控制器最终将数据写入到磁盘的物理扇区中(磁盘上的数据写入可能涉及到旋转延迟、寻道时间等物理过程)。

如果 Java 文件写入想直接刷盘而不是先被写到页缓存中,可以执行 Java 提供了原子文件操作(FileChannel 的 force() 方法),确保写入的内容在物理磁盘上持久化,并且避免操作系统的缓存延迟。

扩展知识

什么是 DMA?

DMA(直接内存访问)是一种用于数据传输的技术,允许外设或外部设备(如硬盘、网络卡、显卡等)直接与系统内存进行交互,而不需要 CPU 的干预。这种方式可以极大地提高数据传输效率,减少 CPU 的负担,优化系统性能。

如果没有 DMA,CPU 会直接参与所有的内存到外设或外设到内存的数据传输。这不仅会占用 CPU 资源,还会导致数据传输速度较慢!

BufferedOutputStream 写入示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class BufferedOutputStreamExample {
public static void main(String[] args) {
// 要写入的目标文件路径
String filePath = "output_large.txt";

// 模拟要写入的数据
StringBuilder data = new StringBuilder();
for (int i = 0; i < 100000; i++) {
data.append("Line " + i + ": mainshiya.\n");
}

// 使用 try-with-resources 语句确保流的正确关闭
try (
FileOutputStream fileOutputStream = new FileOutputStream(filePath);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream)
) {
// 将字符串数据转换为字节数组
byte[] dataBytes = data.toString().getBytes();

// 批量写入数据到 BufferedOutputStream
bufferedOutputStream.write(dataBytes);

// 调用 flush() 将缓冲区中的数据强制写入到磁盘
bufferedOutputStream.flush();

System.out.println("写入完成!");
} catch (IOException e) {
e.printStackTrace();
}
}
}

文件系统的 fsync()fdatasync() 调用

  • fsync() 是一个系统调用,用于强制将一个文件描述符所对应的文件数据从 Page Cache 刷新到磁盘。与 flush() 不同,fsync() 会确保所有数据(不仅仅是文件内容,还有元数据如修改时间)都被写入磁盘。
  • fdatasync() 类似于 fsync(),但是它只会同步文件的内容数据,不包括元数据。所以 fdatasync() 会比 fsync() 更高效一些。
Comments