VFS层将write()系统调用统一转为对inode和page cache的操作,屏蔽文件系统差异;数据仅在调用fsync()/sync()后才落盘,否则停留于内存脏页,由writeback线程按dirty_ratio阈值异步刷回。

数据写入时,VFS 层到底做了什么
Linux 写操作不会直接触达磁盘,而是先经过 VFS(Virtual File System)抽象层。它把 write() 系统调用统一转成对 inode 和 page cache 的操作,屏蔽了 ext4、XFS、Btrfs 等具体文件系统的差异。
关键点在于:只要没显式调用 fsync() 或 sync(),数据就只停留在内存的 page cache 中,尚未落盘。
-
write()返回成功 ≠ 数据已写入磁盘 - 脏页(dirty page)由内核线程
pdflush(旧内核)或writeback(4.0+)异步刷回 -
/proc/sys/vm/dirty_ratio和/proc/sys/vm/dirty_background_ratio控制刷盘触发阈值
ext4 文件系统如何分配磁盘块
ext4 使用 extent(连续块描述)替代早期 ext2/3 的间接块指针,在大文件场景下显著减少元数据开销。一个文件的物理布局由 inode 中的 i_block[] 数组 + extent tree 共同描述。
当需要新块时,ext4 优先在同一个 block group 内分配,以提升局部性;若空间不足,则触发跨 group 搜索,并可能触发 lazy initialization(如未格式化的 block group 被跳过初始化)。
- 每个 block group 包含自己的 bitmap、inode table 和 data blocks,避免全局锁争用
- 使用
chattr +e可强制启用 extent 模式(新建文件默认已启用) - 碎片严重时,
e2fsck -D可重建目录索引,但不整理文件数据块
从 write() 到磁盘扇区:IO 栈逐层穿透
用户态 write() 发起后,路径为:libc → syscall → VFS → filesystem (ext4) → block layer → device driver → disk firmware。其中 block layer 是关键枢纽,负责 IO 合并、排序、限速和队列调度。
常见误区是认为“写得快 = 磁盘快”,其实瓶颈常卡在 block layer 的 queue depth 或 scheduler 策略上:
- SSD 推荐用
none或mq-deadline调度器,避免传统电梯算法引入额外延迟 -
cat /sys/block/sda/queue/scheduler查看当前调度器,echo mq-deadline > /sys/block/sda/queue/scheduler可临时切换 - NVMe 设备默认使用
none(即 bypass scheduler),但部分老内核需手动设置
echo 'vm.dirty_ratio = 30' >> /etc/sysctl.conf echo 'vm.dirty_background_ratio = 5' >> /etc/sysctl.conf sysctl -p
读取时 page cache 命中与绕过的条件
read() 默认走 page cache 路径:先查该文件对应 offset 是否已在内存中;命中则直接拷贝,不发磁盘 IO。但以下情况会绕过 cache:
- 打开文件时指定
O_DIRECT标志,要求 kernel bypass page cache,直接与设备驱动交互 - 使用
posix_fadvise(fd, offset, len, POSIX_FADV_DONTNEED)主动丢弃缓存页 - 内存紧张时,kernel 可能回收 clean page(未修改的缓存页),下次读仍需 IO
O_DIRECT 要求用户缓冲区地址、偏移、长度均按 logical_block_size 对齐(通常是 512B 或 4K),否则 write() 返回 -EINVAL。
strace -e trace=write,read,fsync 和 blktrace -d /dev/sda 能清晰区分是应用层逻辑问题,还是底层 IO 调度或硬件响应慢。page cache 的存在让“读写快”变得廉价,但也让“数据持久性”变成需要主动管理的事。









