在很多IO场景中,我们经常需要确保数据已经安全的写到磁盘上,以便在系统宕机重启之后还能读到这些数据。但是我们都知道,linux系统的IO路径还是很复杂的,分为很多层,每一层都可能会有buffer来加速IO读写。同时,用户态的应用程序和库函数也可能拥有自己的buffer,这又给IO路径增加了一些复杂性。可见,要想保证数据安全的写到磁盘上,并不是简单调一个write/fwrite就可以搞定的。
那么要怎么做呢?很多人会想到很多办法,比如:fflush()、fsync()、fdatasync()、sync()、open()使用O_DIRECT或O_SYNC标志等。嗯,这些手段(或者某些组合)的确可以保证数据安全的持久化,那么它们之间有什么区别呢?fflush()和fsync()有啥区别?O_DIRECT是啥意思,它可以保证数据安全的持久化吗?O_DIRECT和O_SYNC区别什么?O_SYNC和fsync()呢?fsync能完成msync的功能吗?本文将试图理解、解释这些概念的作用和区别。

Linux IO

所谓一图胜千言,为了解析清楚这些概念的区别,我特意画了一张图,仔细看,应该可以清晰的看出它们的作用和区别。

这里重点说一下O_DIRECT和O_SYNC,首先要明确的是,O_DIRECT只是说数据不会经过page cache(一般用在用户态自己管理buffer)而是直接提交给块设备层,但是不会同步等待数据安全写入磁盘之后才返回(比如数据可能还在块层排队或者在磁盘自己的cache中)。而O_SYNC标志,虽然数据还是会写page cache,但是此时会采用write through的策略,并同步等待数据安全写入磁盘后才会返回。因此如果同时使用O_DIRECT和O_SYNC,则表示数据不会经过page cache并同步等待数据安全写入磁盘才返回,当然这样IO的性能会非常低下。
由于O_DIRECT会bypass page cache,因此如果有另一个进程使用普通的方式读文件,有可能会出现数据不一致的现象,这个也需要注意。
为了做一下辅助说明,此处我贴一下我探讨过程中看过的一些资料。首先是引用open系统调用:
http://man7.org/linux/man-pages/man2/open.2.html
相关参数的说明:

以及innodb相关的文档:
https://lwn.net/Articles/457667/

fsync和fdatasync的区别:
http://man7.org/linux/man-pages/man2/fsync.2.html

msync:
http://man7.org/linux/man-pages/man2/msync.2.html

DAX

其实还有一种IO模式,就是DAX(Direct Access ),是不是看上去和O_DIRECT很像。这种模式需要filesystem和block driver都支持才可以,一般主要用在non volatile memory上,本质上也是绕过page cache直接操作设备。DAX本文先不做深入探讨,后面我会自己写一个支持DAX模式的ramdisk块设备驱动,然后格式化为ext4文件系统并-o dax模式挂载,再来详细研究DAX的IO路径。
最后附上Linux在常见场景下的io路径跟踪:
https://my.oschina.net/fileoptions/blog/3061822

Linux系统:保证数据安全落盘的更多相关文章

  1. Linux:保证数据安全落盘

    背景 在很多IO场景中,我们经常需要确保数据已经安全的写到磁盘上,以便在系统宕机重启之后还能读到这些数据.但是我们都知道,linux系统的IO路径还是很复杂的,分为很多层,每一层都可能会有buffer ...

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

    本文转载自Linux/UNIX编程如何保证文件落盘 导语 我们编写程序write数据到文件中时,其实数据不会立马写入磁盘,而是会经过层层缓存.每层缓存都有自己的刷新时机,每层缓存都刷新后才会写入磁盘. ...

  3. Java如何保证文件落盘?

    本文转载自Java如何保证文件落盘? 导语 在之前的文章Linux/UNIX编程如何保证文件落盘中,我们聊了从应用到操作系统,我们要如何保证文件落盘,来确保掉电等故障不会导致数据丢失.JDK也封装了对 ...

  4. 在Linux系统下制作系统启动盘(Ubuntu Linux)

    在Linux系统下制作系统启动盘有两种方法: 1.用dd命令 2.用Linux自带的图形界面工具 Startup Disk Creator 本教程使用第2种方式,用Linux自带的图形界面工具制作系统 ...

  5. linux系统创建windows启动盘

    平时工作中用到linux的操作命令较多,因此为了方便,就给电脑装了双系统,一般工作的时候,都选择进入linux系统.但是今天有件工作之外的事情需要解决下:创建一个windows启动盘.如果按照往常来说 ...

  6. 如何实现Kali linux系统下的U盘启动(小白指导)

    一.准备工作: 声明:这个“操作”并不会影响你原装的系统,真正的即插即用的哦. (1)4GB的U盘<读写速度比较快的> (2)Kali linux镜像文件 (3)软件Universal-U ...

  7. Linux系统下面挂载u盘

    1.先插好u盘到Linux服务器,然后查看u盘挂载到哪个目录下面. [root@localhost ~]# /sbin/fdisk -l 2.挂载到u目录下面 [root@localhost ~]# ...

  8. linux系统无法挂载U盘

    插上U盘 [ 2407.650440] usb 1-3.3: new high speed USB device number 7 using s5p-ehci [ 2407.887332] usb ...

  9. 在linux系统中出现u盘问题 的相关解决方法

    1.显示unknown filesystem type .exfat 可以通过该方法解决: 安装exfat-fuse: 在终端中以管理员身份运行 sudo apt-get install exfat- ...

随机推荐

  1. [Gamma阶段]第三次Scrum Meeting

    Scrum Meeting博客目录 [Gamma阶段]第三次Scrum Meeting 基本信息 名称 时间 地点 时长 第三次Scrum Meeting 19/05/29 大运村寝室6楼 30min ...

  2. 微信小程序怎么获取page/index/main后面的参数

    请求例子:page/index/main?id=123,如何获取123? mpvue框架在onLoad(options)函数里面获取options.id即可. 用的框架不同,onLoad的函数不同.原 ...

  3. win10教育版永久激活密钥 win10教育版激活码 win10教育版产品密钥2018(第三种方法亲测可用!)

    有没有最新win10教育版激活密钥?win10教育版功能比较全面,增加了一些学术上需要的功能.有用户装了win10教育版,提示需要产品密钥,网上找到的win10教育版永久激活码大多失效,这边小编为大家 ...

  4. IDEA的SonarLint插件报错Unable to create symbol table for

    执行sonarLint 报错: Unable to create symbol table for ***File won't be refreshed because there were erro ...

  5. Db2 Terminate Vs Connect Reset , Disconnect

    db2 Terminate and db2 connect Reset both break the connection to a database.                Connect ...

  6. 前端文章索引:HTML+CSS+JS

    1 HTML 1.1 HTML5 HTML5 – 1.基础 HTML5 – 2.新元素 HTML5 – 3.加强版ol HTML5 – 4.canvas 2 CSS 2.1 CSS3 CSS3–1.c ...

  7. pycharm 生成requirements.txt

    在命令行中输入 pip freeze>requirements.txt 1 安装requirements文件中的包 pip install -r requirements.txt

  8. JSON字符串转实体对象

    JSON转实体两种方式 代码片段 ; i < dt.Rows.Count; i++) { //Json字符串 string designJson = dt.Rows[i]["Desig ...

  9. SQL优化关于or与in使用

    网上有很多人都在谈论or与in的使用,有的说二者没有什么区别,其实不然,估计是测试做的不够,其实or的效率为O(n),而in的效率为O(log2n),当基数越大时,in的效率就能凸显出来了. 有人做了 ...

  10. C# 取得某月的最后一天和第一天

    strDate="2019-03" DateTime Date = DateTime.Parse(strDate); //要取得月份的某一天第一天).Date.AddDays( - ...