write() 更易卡住因内核写入路径长且可控性低:需经脏页管理、回写调度、设备队列等多层,而 read() 常命中页缓存或仅单次 I/O;脏页批量回写时进程阻塞,iostat 无负载但 vmstat 显示 pgpgout 飙高。

为什么 write() 比 read() 更容易卡住
根本原因不在磁盘本身,而在内核的写入路径更长、可控性更低。读取通常能从页缓存直接返回(命中时),而写入必须经过脏页管理、回写调度、设备队列等多层缓冲与决策。
常见现象:write() 突然阻塞数秒,strace 显示卡在系统调用里;iostat 却没看到明显 I/O;vmstat 1 中 pgpgout 飙高但 bi(块输入)几乎为 0——这说明脏页正在被内核批量回写,而非你的进程在直接写盘。
-
write()默认是“异步提交到页缓存”,不等落盘;但当缓存快满(vm.dirty_ratio触发)、或调用fsync()/close()时,内核会强制同步刷脏页,此时你的线程就会卡住 -
read()若缓存命中,全程不碰磁盘;即使未命中,也只触发一次预读+单次 I/O,路径短、无累积效应 - SSD 或 NVMe 上写放大、GC 延迟也会在回写阶段集中暴露,但用户感知为“写比读慢”
O_DIRECT 能绕过缓存加速写入吗
不能一概而论——它绕过了页缓存,但也放弃了内核的预读、合并、延迟调度等优化,实际效果取决于 workload 和硬件。
适用场景:已自行实现高效缓存/预取逻辑(如数据库)、写入流高度顺序且大小对齐(如 4K 对齐的大块日志)。
GNU makefile中文手册 pdf,文比较完整的讲述GNU make工具,涵盖GNU make的用法、语法。同时重点讨论如何为一个工程编写Makefile。阅读本书之前,读者应该对GNU的工具链和Linux的一些常用编程工具有一定的了解。诸如:gcc、as、ar、ld、yacc等本文比较完整的讲述GNU make工具,涵盖GNU make的用法、语法。重点讨论如何使用make来管理软件工程、以及如何为工程编写正确的Makefile。 本手册不是一个纯粹的语言翻译版本,其中对GNU make的一些语法
- 必须确保用户缓冲区地址和长度都按存储设备逻辑块大小对齐(通常是 512B 或 4K),否则
write()直接返回-EINVAL - 每次
write()都真实下发 I/O 请求,无法合并;小块随机写 +O_DIRECT会让 IOPS 爆表、延迟飙升 -
O_DIRECT不保证元数据(如文件大小更新)落盘,fsync(O_SYNC)仍需额外调用
哪些配置项真正影响写入延迟
关键不是调大缓存,而是控制脏页生成节奏和回写时机。默认值适合吞吐优先场景,对低延迟写入反而有害。
-
vm.dirty_ratio(默认 80):触发同步回写的脏页百分比上限;建议降至 30–50,避免突发刷盘阻塞 -
vm.dirty_background_ratio(默认 10):后台线程开始异步回写的阈值;建议设为 5–15,让刷盘更平滑 -
vm.dirty_expire_centisecs(默认 3000 = 30 秒):脏页最大驻留时间;若应用要求强一致性,可压到 500(5 秒)以内 -
/proc/sys/vm/swappiness设为 0 可减少因内存压力导致的意外换出干扰写入路径
如何快速验证是否是内核回写导致的写延迟
不用上 perf 或 ftrace,几个简单命令就能定位:
- 运行
watch -n 1 'cat /proc/meminfo | grep -E "Dirty|Writeback"':观察Dirty值是否周期性冲高后骤降(典型回写行为) - 执行
echo 1 > /proc/sys/vm/drop_caches后再测写入——如果延迟大幅下降,说明原先是脏页积压所致(注意:这仅用于诊断,勿在生产环境乱用) - 用
perf record -e 'syscalls:sys_enter_write' -a sleep 10抓取写系统调用耗时分布,再perf script查看是否有大量 >100ms 的样本
真正难处理的是混合负载:一边有日志持续写入,一边有备份进程 dd if=/dev/sda of=/backup.img 扫盘——后者会污染页缓存并触发全局回写,导致前者写入抖动。这种干扰很难靠单个参数消除。









