You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用write() API对文件全部或部分内容执行原子RMW写入操作

原生write() API无法满足你的两类原子性要求

首先明确:标准POSIX write() 本身不支持原子RMW(读-修改-写)语义,也没法同时保障你要求的故障原子性和并发原子性:

  • 并发原子性层面:只有O_APPEND模式下的追加写入、且写入大小不超过PIPE_BUF时才被POSIX保证原子,普通位置的覆盖写入完全不保证多进程并发下不会出现内容穿插,更不可能保证两个写入操作最终只会完整保留其中一个的内容。
  • 故障原子性层面:默认write()写入的是操作系统页缓存,没有显式刷盘的话进程崩溃/系统掉电就会丢失数据;即使调用fsync()刷盘,也无法避免写入中途掉电出现新旧内容混合的问题。

符合要求的类CAS写入方案

以下是几种不同场景下的可行实现:

1. 全量内容原子替换(兼容性最强,通用所有POSIX系统)

这个方案是工业界最常用的文件原子更新实现,完全满足你要的两类原子性:

  • 先将完整的新内容写入到和目标文件同挂载点的临时文件中,调用fsync()将临时文件的内容、元数据全部刷入持久化存储,保证故障重启后临时文件内容完整。
  • 调用rename()系统调用直接用临时文件覆盖目标文件路径。POSIX标准明确保证rename()是原子操作:
    • 并发场景下多个进程同时执行替换操作,最终只会保留最后一次成功rename的文件内容,不会出现不同写入的内容混合,符合最终值要么是A要么是B的要求。
    • 故障场景下rename()要么完全生效、要么完全不生效,不会出现半修改的中间状态,只要临时文件已经刷盘,操作成功后就算重启目标文件也会是完整的新内容。

2. 部分内容原子RMW实现

如果不需要修改整个文件,只需要修改部分片段,可以选以下两种方案:

方案A:基于文件锁封装CAS逻辑

适配所有POSIX系统,只需要所有访问文件的进程遵守统一的加锁规则即可:

  • 调用fcntl()对目标文件加排他锁,保证同一时间只有一个进程可以修改文件。
  • 加锁成功后读取你要修改的片段的当前值,判断是否符合你的修改前置条件。
  • 条件满足则写入新内容,调用fsync()刷盘后释放锁。
    如果有进程不遵守加锁规则直接写文件,原子性会被打破。
方案B:文件系统特有原子写入接口

性能更高但兼容性有限,主流本地文件系统(ext4、XFS、Btrfs等)都支持:

  • 使用带RWF_ATOMIC标志的pwritev2()系统调用,只要写入大小不超过当前文件系统支持的原子写入上限(通常为1个物理页面大小,部分文件系统支持更大的块),就能保证写入操作是原子的,并发场景下不会出现内容穿插。
  • 搭配RWF_DSYNC或RWF_SYNC标志可以直接让写入返回时就完成落盘,不需要额外调用fsync(),同时满足故障原子性要求。

目前通用操作系统没有原生的文件级compare-exchange系统调用,如果你需要严格的“只有文件内容符合预期时才写入”的语义,可以在文件头部存储版本号,结合上面的全量替换/加锁方案自己封装实现:写入前先读版本号,写入时将版本号+1,修改时仅当当前版本号和你读取的版本号一致时才提交修改。

内容的提问来源于stack exchange,提问作者Curious

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.14 01:57:03