IO写入的基本过程
a. 定位用户数据
b. 将用户数据拷贝至内核中(page cache)
先解释一下page cache。page cache是文件在内存中的缓存,打开文件时,先要把文件加载进pache cache,写入时也是一样,先写入page cache,再由page cache刷入磁盘。一般来说,page cache中的数据也是文件的一部分,所以如果数据写入了page cache,就可以认为io操作已安全完成。
上述的过程是一个非常简化的版本,实际的一些api比如fwrite()的实现可能是这样的
---用户层---
a. 定位用户数据
b. 将用户数据拷贝至函数的缓存中(函数调用返回)
---vfs层---
c. 将函数缓存拷贝至内核缓存(page cache) (显式或隐式调用fflush() )
d. 将page cache中的脏数据写入磁盘数据区(调用硬盘驱动)
e. 将inode cache中的脏数据写入磁盘inode区(调用硬盘驱动)
---存储控制器层---
到目前为止,写入基本完成,数据被存储控制器接管,由于存储控制器自带板载电容,因此可以认为数据已固化
f. 从板载cache(比如ssd raid 卡)中读取数据
g. 将数据写入磁盘介质
对于linux的write()函数来说,调用在c步就返回了。
而对于fwrite()这种经过一次封装的函数来说,它在write之上增加了一层缓冲,也就是说调用fwrite会在b步返回,为了保证数据写入文件,使用fwrite时,可以调用fflush()函数来保证调用到第c步。如果认为此时存储于操作系统的page cache仍不安全,可以继续调用fsync()来保证调用到第e步。(如果到了这里仍然觉得数据不够安全,可以将存储控制器的模式更改为write through,这样可以保证第g步完成后才返回)。
如果打开文件时采用了direct io的方式,可以绕开对于page cache的操作,就是绕过了d步,但是,e步并没有被绕过,因此,使用direct io方式时,为了保证数据的绝对安全,依旧需要调用fsync()来保证文件的元数据(inode等信息)写入磁盘(可以在open时增加O_SYNC,但这样会严重影响效率)。
顺便提一下,python的write函数应该是基于fwrite的封装,因此需要调用flush()来保证数据写入文件。
(原文时间2014-10-11)