主旨

原子写要求对于写下去的文件,要么全部落盘,要么全部没有落盘。

只有CP和fsync才能保证文件的数据是落盘的,因此原子写的语义需要借助fsync实现。也即是原子写的文件通过fsync落盘,并在恢复的时候要么不恢复;要么全恢复。

文件写入的原子性

文件系统层面的原子性参看事务的原子性与文件系统的崩溃一致性 。对于单个文件来说,必须通过fsync来保证落盘。

普通文件和原子写文件的区别

对于普通文件,完成fsync可以保证数据落盘,但是如果在fsync途中断电,会导致文件恢复出错。

原子写的文件有更高的一致性要求,也即不允许文件恢复出错。要么不恢复,要么必须完全恢复。

在实现上,普通文件通过fsync落盘的每个数据块和inode都具有fsync标记,而原子写的文件只有落盘的最后一个数据块才具有fsync标记。

原子写入文件的过程

这个过程在网上几乎无法找到资料,接下来自己分析。

flowchart
	A[hmfs_ioctl]--为原子写做准备-->B[f2fs_ioc_start_atomic_write]
	A--内存中文件落盘-->C[f2fs_ioc_commit_atomic_write]
	B--加入原子文件列表-->D["***&sbi->inode_list[ATOMIC_FILE]***"]
	B-->E[原子文件计数++
	设置文件***FI_ATOMIC_FILE标记***]
	C--有***FI_ATOMIC_FILE,写脏页-->G[hmfs_commit_inmem_pages]--写inode-->H[fsync]
	C--无FI_ATOMIC_FILE-->I[fdatasync]***

f2fs_ioc_start_atomic_write为原子写做准备

要原子写一个文件,首先通过f2fs_ioc_start_atomic_write做准备。

  1. mnt_want_write_file通过文件指针获取挂载点的写权限(?)

    文件系统可以被以只读或读写模式挂载。如果某个进程试图在只读挂载的文件系统上进行写操作,系统会阻止这类操作。而 mnt_want_write_file 在写操作之前被调用,以检查挂载点是否具有写权限,确保后续的写操作可以顺利进行。

  2. hmfs_balance_fs控制GC,保证文件系统有足够空间写入文件。

  3. inode_lock锁住要写的文件,inode_lock是一种自旋锁,也就是会卡住进程直到获取到锁。inode_lock能锁住文件的元数据,用于直接修改元数据或写入文件导致大小变化的情况。

  4. down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);防止GC线程操作文件。

  5. 文件必须是干净的,如果有脏页,则将脏页下刷,开启原子写失败。