- 系统调用层和虚拟文件系统层
- 调用 read/write 进行读写 → vfs_read/write → __vfs_read/write
- 打开文件时创建 struct file, 其中有 file_operations, 虚拟文件系统调用 operations 中的 read/write
- ext4 文件系统层
- 调用到 generic_file_read/write_iter, 其中判断是否需要使用缓存
- 缓存, 即内存中一块空间, 可分为两类 I/O
- 缓存 I/O: 默认模式, 读操作先检测缓存区中是否有, 若无则从文件系统读取并缓存; 写操作直接从用户空间赋值到内核缓存中, 再由 OS 决定或用户调用 sync 写回磁盘
- 直接 I/O: 程序直接访问磁盘, 不经过缓存
- 直接 I/O 过程:
- 读: 若设置了 IOCB_DIRECT, 调用 address_space 的 direct_io 直接读取硬盘( 文件与内存页映射) ; 若使用缓存也要调用 address_sapce 进行文件与内存页的映射
- 写: 若设置了 IOCB_DIRECT, 调用块设备驱动直接写入磁盘
- 带缓存写过程
- 在 while 循环中, 找出写入影响的页, 并依次写入, 完成以下四步
- 每一页调用 write_begin 做准备
- 将写入内容从用户态拷贝到内核态
- 调用 write_end 完成写入
- 查看脏页 (未写入磁盘的缓存) 是否过多, 是否需要写回磁盘
- write_begin 做准备
- ext4 是日志文件系统, 通过日志避免断电数据丢失
- 文件分为元数据和数据, 其操作日志页分开维护
- Journal 模式下: 写入数据前, 元数据及数据日志必须落盘, 安全但性能差
- Order 模式下: 只记录元数据日志, 写日志前, 数据必须落盘, 折中
- Writeback 模式下: 仅记录元数据日志, 数据不用先落盘
- write_begin 准备日志, 并得到应该写入的缓存页
- 内核中缓存以页为单位, 打开文件的 file 结构中用 radix tree 维护文件的缓存页
- iov_iter_copy_from_user_atomic 拷贝内容, kmap_atomic 将缓存页映射到内核虚拟地址; 将拥护他数据拷贝到内核态; kunmap_aotmic 解映射
- write_end, 先完成日志写入 并将缓存设置为脏页
- 调用 balance_dirty_pages_ratelimited 若发先脏页超额, 启动一个线程执行回写.
- 回写任务 delayed_work 挂在 bdi_wq 队列, 若delay 设为 0, 马上执行回写
- bdi = backing device info 描述块设备信息, 初始化块设备时回初始化 timer, 到时会执行写回函数
- 另外其他情况也会回写
- 用户调用 sync 或内存紧张时, 回调用 wakeup_flusher_threads 刷回脏页
- 脏页时间超过 timer, 及时回写
- 带缓存读
- generic_file_buffered_read 从 page cache 中判断是否由缓存页
- 若没则从文件系统读取并预读并缓存, 再次查找缓存页
- 若有, 还需判断是否需要预读, 若需要调用 page_cache_async_readahead
- 最后调用 copy_page_to_user 从内核拷贝到用户空间
展开