多进程场景下Linux常规文件原子性:是否会出现新旧值反复切换?
核心疑问:文件写入未刷盘时是否会出现"时间回溯"读取现象?
场景背景
进程A通过多次调用write(2)写入数据,在执行close、fsync(2)或fdatasync(2)刷盘前,进程B对同一写入区域调用read(2)读取:
- POSIX.1标准保证同一系统下进程B只会看到完整的写入内容,但实际常见文件系统(如ext4)有时会出现撕裂写入(是否发生取决于页大小)。
- 由于极少有文件是通过单次
write完成写入的,无锁场景下需要处理新旧数据混合的撕裂情况,甚至可能出现零值区域。
核心问题
在可靠本地文件系统中,执行fsync前,进程是否可能先看到新值,再看到旧值,之后又回到新值(即“时间回溯”现象)?(假设数据由HMAC等机制保护,可避免撕裂边界的系统输出被误判为真实值)
回答
不会出现这种“时间回溯”现象,核心原因如下:
- 从内存页缓存的机制来看,Linux文件系统的页缓存采用写时复制+单版本结构:当进程A发起写入时,会先修改页缓存中的对应页面(若页面原本只读,会先复制出可写私有页),一旦修改完成,后续所有对该页面的读取(包括进程B的
read)都会看到更新后的内容,不会再回退到旧版本。 - 所谓“撕裂写入”是指部分页面被刷到磁盘后,后续读取可能混合内存中未刷盘的新页面和磁盘上的旧页面,但这是“新旧混合”而非“时间回溯”——你可能先看到部分新数据,再看到另一部分旧数据,但不会出现已经看到过的新数据又变回旧数据的情况。
- POSIX标准的“完整写入”保证,是指单个
write调用写入的字节要么全部可见,要么全部不可见,但多次write的结果是按顺序叠加的,一旦某段数据的页缓存被更新,就不会再回到之前的状态。
简单总结:页缓存的更新是单向的,一旦某块内存中的数据被修改,后续读取只会看到这个新版本或更晚的版本,绝不会倒退回旧版本。
内容的提问来源于stack exchange,提问作者Dan
相关产品推荐
相关产品推荐

