Linux下C++使用pwrite并行写文件:Ubuntu与macOS执行差异排查
多线程pwrite串行执行问题:原因与解决办法
原因分析
Ubuntu上出现线程串行结束的核心原因是文件系统与内核层的锁竞争差异:
- ext4文件系统特性:Ubuntu默认使用ext4,其默认的日志模式(
data=ordered)会对文件元数据(比如块分配)加全局锁。当多个线程通过pwrite写入未预分配的文件区域时,每个线程都需要向内核申请新的磁盘块,而这些块分配操作会被串行化,导致线程看起来依次完成。 - 磁盘调度器差异:Ubuntu默认的磁盘调度器(如
mq-deadline)对机械盘会做排序优化,可能将并行写请求合并串行处理;而macOS的调度器对SSD和并行IO的支持更激进,能更好地并行处理离散写请求。 - 缓存机制区别:macOS的文件缓存层对多线程并行写的并发处理更高效,而ext4的页缓存在处理多线程写不同偏移时,可能出现局部锁竞争,拖慢并行效率。
对比之下,macOS的APFS/HFS+文件系统本身设计支持更细粒度的元数据锁,且磁盘调度器更适配多线程并行IO,所以线程能几乎同时完成。
解决办法
针对Ubuntu上的问题,可以从以下几个方向优化:
- 预分配文件空间:在启动线程前,先用
fallocate()或lseek()+空写的方式预分配好所有需要写入的文件空间。这样线程执行pwrite时,内核不需要实时分配磁盘块,彻底避免元数据锁竞争。示例代码片段:// 预分配size字节的空间 if (fallocate(fd, 0, 0, size) == -1) { perror("fallocate failed"); exit(EXIT_FAILURE); } - 调整ext4挂载参数:修改文件系统挂载选项,降低日志带来的锁开销:
- 临时生效:执行
sudo mount -o remount,nojournal /your/mount/point关闭日志功能 - 永久生效:编辑
/etc/fstab,将对应ext4分区的挂载选项改为defaults,nojournal或data=writeback(writeback模式下日志仅记录元数据,不强制数据写入顺序,并发性能更好)
- 临时生效:执行
- 调整磁盘调度器:
- 对于SSD,推荐使用
none调度器(无排序,直接转发请求到设备):执行echo none | sudo tee /sys/block/<dev>/queue/scheduler - 对于机械盘,可尝试
bfq调度器,它对多线程IO的调度更公平
- 对于SSD,推荐使用
- 升级内核版本:较新的Linux内核(5.10+)对ext4的并行写和元数据锁做了不少优化,升级内核能缓解锁竞争问题
内容的提问来源于stack exchange,提问作者Jerry
相关产品推荐
相关产品推荐

