数据写入到落盘的过程

1,102 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第5天,点击查看活动详情 >>

编写程序write数据到文件中是,数据不会立即写入磁盘,而是会经过层层缓存。缓存是为了加速读写操作,避免频繁的io开销,但是保存在缓存中的数据,遇到宕机或者是别的故障,存在数据丢失的问题。所以操作系统提供了保证文件落盘的机制。

  • 文件数据从应用程序写入磁盘,需要经过多个缓冲区:应用本身的缓冲区,库的缓冲区,操作系统缓冲区,磁盘缓冲区

为什么需要缓冲区——减少系统调用的次数

一般的操作:库函数缓存刷新时,会调用write系统调用写入内核空间,内核同样维护了一个页缓存(page cache),操作系统会在合适的时间把脏页的数据写入磁盘。磁盘也可能维护一个缓存,只有写入了磁盘的持久存储物理介质上,数据才真正落盘。

用户空间缓冲区

应用本身的缓冲区

开发者自己刷新,调用库函数写入到库函数的缓冲区中。

库的缓冲区

C标准库的fflush,JDK的OutputStream#flush会强制将缓冲区数据写入操作系统

内核缓冲区

操作系统缓冲区

为什么需要高速缓存或页面高速缓存——减少磁盘读写次数。

用户写入系统的数据先写入系统缓冲区,系统缓冲区写满后,将其排入输出队列,然后得到队首时,才进行实际的IO操作。这种输出方式被称为延迟写

UNIX系统提供了三个系统调用来执行刷新内核缓冲区:syncfsyncfdatasync

  • sync函数只是将所有修改过的块缓冲区排入输出队列就返回,不保证一定落盘。

  • fsync指定文件落盘,堵塞直至返回,可以刷新文件数据+元数据缓冲区

  • fdatasync可以刷新文件数据,在不影响读取的情况下,可以不刷新文件元数据,性能更好一些

    • 如果更新不修改文件长度,后者比前者能够少一次磁盘写入

除此之外,open打开文件时,可以设置同步相关的标志位O_SYNCO_DSYNC

  • O_SYNC标志位可以在每次write后自动调用fsync
  • O_DSYNC标志位可以在每次write后自动调用fdatasync

补充:

对于新建文件,还需要在父目录上调用fsync

对于覆盖现有文件,最好先tmp,然后写tmp,fsync,rename,最后对父目录调用fsync

存储设备(磁盘)缓冲区

磁盘缓冲区

  • 文件系统支持开启/关闭写屏障write barriers,linux默认开启

    • 如果开启写屏障,则fsync/fdatasync可以保证文件写入磁盘的持久化设备中
    • 如果关闭写屏障,则fsync/fdatasync只能保证文件写入磁盘,此时文件可能存在于磁盘的缓存中

参考

Linux/UNIX编程如何保证文件落盘