fsync和fdatasync是否一定能保证数据写入磁盘不会丢?

【page cache由来】    

    在UNIX和linux系统上,在数据被写入到磁盘上之前,内核通常先将该数据复制到其中一个缓冲区中,如果该缓冲区尚未写满,则并不将其排入输出队列,而是等待其写满或者当内核需要重用该缓冲区以便存放其他磁盘块数据时,再将该缓冲排入输出队列,然后待其到达队首时,才进行实际的I/O操作。这种输出方式被称为延迟写(delayed write)。

【sync函数】

    sync函数只是将所有修改过的块缓冲区排入写队列,然后就返回,它并不等待实际写磁盘操作结束。
通常称为update的系统守护进程会周期性地(一般每隔30秒)调用sync函数。这就保证了定期冲洗内核的块缓冲区。命令sync(1)也调用sync函数。

【fsync与fdatasync函数】

    fsync函数只对由文件描述符filedes指定的单一文件起作用,并且等待写磁盘操作结束,然后返回。fsync可用于数据库这样的应用程序,这种应用程序需要确保将修改过的块立即写到磁盘上。
    fdatasync函数类似于fsync,但它只影响文件的数据部分。而除数据外,fsync还会同步更新文件的属性。

    linux官方文档的解释如下:

    fsync和fdatasync是否一定能保证数据写入磁盘不会丢?_第1张图片

 

    但是fsync和fdatasync一定非常可靠吗?答案是不一定!

    因为磁盘(或RAID卡)自身通常会有硬件缓存机制,对于写操作,有write back和write through两种机制,前者将数据写至缓存就会返回,而后者则会将数据写到磁盘介质上。当使用write back机制时,fsync刷的文件数据可能只是写到磁盘缓存就返回了,导致从应用看来,写数据到磁盘的开销很小(实际上并未执行磁盘写操作);所以,使用write back机制时,即使上层应用显式fsync成功,数据也是可能丢失的,比如缓存里的数据还未刷到磁盘时掉电了,因此需要备用电池(BBU,Battery backup unit)来避免掉电时缓存数据丢失。

【开启barrier机制】

    如果想要绝对保险,确保调用fsync或fdatasync之后数据一定能落地到磁盘上,则可以开启linux内核的write barrier机制,它可以控制在IO操作之前和之后刷新缓存来达到一定能落地磁盘的目的。Ext4 默认启用 barrier,只有当 barrier 之前的数据全部写入磁盘,才能写 barrier 之后的数据。(可通过 "mount -o barrier=0" 命令禁用该特性。)对于该特性的解释如下:

    磁 盘上配有内部缓存,以便重新调整批量数据的写操作顺序,优化写入性能,因此文件系统必须在日志数据写入磁盘之后才能写 commit 记录(类似WAL),若 commit 记录写入在先,而日志有可能损坏,那么就会影响数据完整性。Ext4 默认启用 barrier。

【mmap函数】

    对于内存映射(mmap)的文件数据,msync的功能与fsync类似,将内存映射数据刷到磁盘,msync使用时有3个标志:

    1.MS_SYNC,数据同步刷到磁盘后返回(可能只是写到磁盘缓存)

    2.MS_ASYNC, 对于更新的文件数据会提交IO操作到底层,但不会等IO操作执行完成,而是立即返回

    3.MS_INVALIDATE,更新文件对应的其它映射数据(如内存区域M1、M2都映射了文件F的数据,如果在msync M1的时候指定了该标记,则M2内存区域里的数据也会被更新)。

和fsync同理,要想映射内存msync后被持久化到磁盘,需要使用MS_SYNC标记;而msync调用后数据是否一定持久化,则要看存储设备使用的缓存机制以及内核write barrier是否启用。

你可能感兴趣的:(linux)