目录
1.完整文件写入流程
linux和Windows在数据IO的过程上都大同小异也就是一些东西的命名不同而已,架构上都大差不差。此处以Linux作为示例,大致过程:
- CPU 发出写入指令
- 数据从用户空间拷贝到内核空间(内存)
- 虚拟内存与物理内存映射
- 内核调度 I/O 操作
- 块设备驱动与 DMA 传输
1.CPU 发出写入指令
程序调用 write() 的系统调用,请求将数据写入文件。 内核接收到系统调用后,进入内核态,准备处理 I/O 请求。
2.数据从用户空间拷贝到内核空间(内存)
程序进程通过虚拟内存+MMU(也就是地址编制+映射那一套)找到实际的内存地址。用户程序的数据位于用户空间内存中。 内核通过 copy_from_user() 将数据复制到内核空间的页缓存(Page Cache)中。 Page Cache 是 Linux 文件系统用于缓存文件数据的一种机制,提高读写效率。
3.虚拟内存与物理内存映射
用户进程访问的是虚拟地址空间,需要通过 MMU(Memory Management Unit)转换为物理地址。 内核维护页表(Page Table),记录虚拟地址到物理地址的映射关系。 数据最终被存储在物理内存中的某个页帧(Page Frame)。
4.内核调度 I/O 操作
数据写入 Page Cache 后,不一定立即落盘,可能被标记为“脏”(Dirty)。 内核根据策略(如定期刷新、内存压力等)决定何时将数据写入磁盘。 内核调用文件系统的 writepage 方法,将数据排入 I/O 队列。
5.块设备驱动与 DMA 传输
数据从内存通过 DMA(Direct Memory Access)方式传输到硬盘控制器,不经过 CPU。 内核通知硬盘控制器要写入的数据在内存中的物理地址和大小。
2.page cache
什么是page cache:
page cache是专门用来存频繁用到的磁盘中的数据的内存区域。每个被打开的文件,也就是判定为要频繁使用的文件,被划分成多个固定大小的块(通常是4KB),一个块被称为一“页”(Page)
文件读取流程:
- 用户调用 read(fd, buf, len)。
- 内核进入内核态,查找对应文件的 address_space。
- 检查请求的数据是否已经在 Page Cache 中:
- 如果命中:直接复制到用户缓冲区。
- 如果未命中:触发缺页中断(Page Fault),从磁盘加载数据到 Page Cache,再复制给用户空间。
- 返回用户态。
文件写入流程:
-
用户调用 write(fd, buf, len)。
-
内核检查是否有对应的 Page Cache 页:
-
有则直接更新内存页;
-
没有则分配新页并拷贝数据。
-
-
标记该页为“脏页”(Dirty)。
-
异步或同步地将脏页刷新到磁盘(取决于文件打开标志如 O_SYNC)。
文件写入流程中有个关键点就是如何找到对应的page cache:
每个打开的文件都有一个 inode,其中包含一个 struct address_space 成员(即 i_mapping),用于管理该文件对应的缓存页。 当你调用 write() 或使用 mmap 写入文件时,内核会根据你要写入的偏移量(offset)计算出要操作的页号(index),然后进行如下操作:
pgoff_t index = offset >> PAGE_SHIFT; // 计算页号 struct page *page = find_get_page(mapping, index);
其次是如何找到要写入的文件中的位置:
会有一个偏移量offset, 即相对于文件开头的字节数,通过这个offset能算出page cahce中的page号(页号),这个页就是要写入的位置。
脏页回写磁盘:
内核使用一组后台线程(如 pdflush 或 kswapd)定期将脏页写入磁盘。
页面回收:
当内存不足时,内核会尝试回收 Page Cache 中未修改的页(Clean Pages)。对于脏页(Dirty Pages),需要先写回磁盘才能释放内存。
3.文件完整读取过程
大致过程:
-
用户程序发起读取请求;
-
内核处理 I/O 请求;
-
判断是否命中 Page Cache;
-
如果未命中,则从磁盘加载数据;
-
数据最终返回给用户空间。
详细过程: