O_DIRECT和O_SYNC是系统调用open的flag参数。通过指定open的flag参数,以特定的文件描述符打开某一文件。

这两个flag会对写盘的性能有很大的影响,因此对这两个flag做一些详细的了解。

先看一个open函数的使用例子.

/* Open new or existing file for reading and wrting,
sync io and no buffer io; file permissions read+
write for owner, nothing for all others */
fd = open("myfile", O_RDWR | O_CREAT | O_SYNC | O_DIRECT, S_IRUSR | S_IWUSR);
if (fd == -)
errExit("open");
O_DIRECT: 无缓冲的输入、输出。
O_SYNC:以同步IO方式打开文件。
下面对这两个flag做一些详细的说明。
 

一,O_DIRECT,绕过缓冲区高速缓存,直接IO

直接IO:Linux允许应用程序在执行磁盘IO时绕过缓冲区高速缓存,从用户空间直接将数据传递到文件或磁盘设备,称为直接IO(direct IO)或者裸IO(raw IO)。
应用场景:数据库系统,其高速缓存和IO优化机制均自成一体,无需内核消耗CPU时间和内存去完成相同的任务。
使用直接IO的弊端:可能会大大降低性能,内核对缓冲区告诉缓存做了不少优化,包括:按顺序预读取,在成簇磁盘块上执行IO,允许访问同一文件的多个进程共享高速缓存的缓冲区。
使用方法:在调用open函数打开文件或设备时指定O_DIRECT标志。
注意可能发生的不一致性:若一进程以O_DIRECT标志打开某文件,而另一进程以普通(即使用了高速缓存缓冲区)打开同一文件,则由直接IO所读写的数据与缓冲区高速缓存中内容之间不存在一致性,应尽量避免这一场景。
 
使用直接IO需要遵守的一些限制:
  • 用于传递数据的缓冲区,其内存边界必须对齐为块大小的整数倍
  • 数据传输的开始点,即文件和设备的偏移量,必须是块大小的整数倍
  • 待传递数据的长度必须是块大小的整数倍。

不遵守上述任一限制均将导致EINVAL错误。

二,O_SYNC,以同步方式写入文件

功能:强制刷新内核缓冲区到输出文件。这是有必要的,因为为了数据安全,需要确保将数据真正写入磁盘或者磁盘的硬件告诉缓存中。

我们先熟悉一下同步IO相关定义和系统调用。

同步IO数据完整性和同步IO文件完整性

同步IO的定义:某一IO操作,要么已成功完成到磁盘的数据传递,要么被诊断为不成功。
SUSv3定义的两种同步IO完成类型(此处用英文,因为译者也忍无可忍用了原文…)
  • synchronized IO data integrity completion:确保针对文件的一次更新传递了足够的信息(部分文件元数据)到磁盘,以便于之后对数据的获取。
  • synchronized IO file integrity completion:确保针对文件的一次更新传递了所有的信息(所有文件元数据)到磁盘,即使有些在后续对文件数据的操作并不需要。

用于控制文件IO内核缓冲的系统调用

1 fsync

作用:fsync()系统调用将使缓冲数据和fd相关的所有元数据都刷新到磁盘上。调用fsync会强制使文件处于Synchronized IO file integrity completion状态。
函数声明:

#include
int fsync(int fd);

函数返回值:
  • 0: success
  • -1: error
返回时间:仅在对磁盘设备(或者至少是其高速缓存)的传递完成后,fsync()调用才会返回。
 
2 fdatasync
作用:fdatasync()系统调用的作用类似fsync(),只是强制文件处于synchronized IO data integrity compeletion状态。
函数声明:

#include
int fdatasync(int fd);
 

函数返回值:
  • 0: success
  • -1: error
与fsync的区别:fdatasync()可能会减少磁盘操作的次数,由fsync()调用请求的两次变成一次。例如,修改了文件的数据,而文件大小不变,那么调用fdatasync调用请求只强制进行了数据更新,相比之下,fsync()调用会强制将元数据传递到磁盘上,而元数据和文件数据通常驻留在磁盘的不同区域,更新这些数据需要反复在整个磁盘上执行寻道操作。
 
3 sync系统调用
作用:sync()系统调用会使包含更新文件信息的所有内核缓冲区(即数据块、指针块、元数据等)刷新到磁盘上。
函数声明:

#include
void sync(void);
 

细节:若内容发生变化的内核缓冲区在30s内未经显式方式同步到磁盘上,则一条长期运行的内核线程会确保将其刷新到磁盘上。这一做法是为了规避缓冲区与相关磁盘文件内容长期处于不一致状态。
 
4 使所有写入同步:O_SYNC
调用open()函数时,如制定O_SYNC标志,则会使所有后续输出同步。

fd = open(pathname, O_WRONLY | O_SYNC)
 


作用:调用open后,每个write调用会自动将文件数据和元数据刷新到磁盘上,即按照Synchronized IO file integrity completion的要求执行写操作。

 
5 有无O_SYNC性能对比
场景:将一百万字节写入一个ext2文件系统上的新创建文件,比较写入时间。
对比结果:
从结果中可以得到的结论:
  • 采用O_SYNC标志(或者频繁调用fsync(), fdatasync()或sync())对性能影响极大。
  • 性能下降的直接表现为运行总用时大为增加:在缓冲区为1字节的情况下,运行时间相差1000多倍。
  • 以O_SYNC标志执行写操作时运行总用时和CPU时间之间的巨大差异(1030 - 98.8),原因是系统在每个缓冲区中将数据向磁盘传递时会把程序阻塞起来。
 

三,IO缓冲层次关系

先总结一下stdio函数库和内核采用的缓冲这两级缓冲,然后用图说明两层缓冲机制和各种缓冲类型的控制机制。
  • 首先,通过stdio库将用户数据传递到stdio缓冲区,该缓冲区位于用户态内存区。
  • 当缓冲区填满,stdio库会调用write()系统调用,将数据传递到内核高速缓冲区,该缓冲区位于内核态内存区。
  • 最终,内核发起磁盘操作。
该层次结构如下图所示
 
上图中,左侧虚线方框中为可于任何时刻显式强制刷新各类缓冲区的调用。
右侧所示为促使刷新自动化的调用:通过禁用stdio的缓冲,和在文件输出类的系统调用中启用同步,从而使每个write()调用立刻刷新到磁盘。
 

四,小结

输入输出数据的缓冲由内核和stdio库完成。有时可能希望阻止缓冲,但这需要了解其对应用程序性能的影响。
可以使用各种系统调用和库函数来控制内核和stdio缓冲,并执行一次性的缓冲区刷新。
在Linux环境下,open()所特有的O_DIRECT标识允许特定应用跳过缓冲区高速缓存。
 

O_DIRECT与O_SYNC区别(转)的更多相关文章

  1. UNIX高级环境编程(14)文件IO - O_DIRECT和O_SYNC详解 < 海棠花溪 >

    春天来了,除了工作学习,大家也要注意锻炼身体,多出去运动运动.  上周末在元大都遗址公园海棠花溪拍的海棠花.   进入正题. O_DIRECT和O_SYNC是系统调用open的flag参数.通过指定o ...

  2. Linux系统:保证数据安全落盘

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

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

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

  4. 关于innodb_flush_log_at_trx_commit、innodb_flush_method、innodb_log_block_size和fsync()、O_DIRECT、iops、云盘的关系与总结

    想着整理关于innodb_flush_log_at_trx_commit.innodb_flush_method.innodb_log_block_size和fsync().O_DIRECT.iops ...

  5. sync fsync fdatasync ---systemtap跟踪

    aa.stp: probe kernel .function ( "sys_sync" ) { printf ( "probfunc:%s fun:%s\n", ...

  6. UNIX高级环境编程1

    UNIX高级环境编程1 故宫角楼是很多摄影爱好者常去的地方,夕阳余辉下的故宫角楼平静而安详. 首先,了解一下进程的基本概念,进程在内存中布局和内容. 此外,还需要知道运行时是如何为动态数据结构(如链表 ...

  7. 详解linux io flush

    通过本文你会清楚知道 fsync().fdatasync().sync().O_DIRECT.O_SYNC.REQ_PREFLUSH.REQ_FUA的区别和作用. fsync() fdatasync( ...

  8. Redis persistence demystified - part 1

    关于Redis我的一部分工作是阅读博客,论坛以及twitter时间线(time line).对于开发者来说,能够了解用户社区,非用户社区如果理解他正在开发的产品是非常重要的.据我所知,持久化特性是最易 ...

  9. KVM几种缓存模式

    原文在这里: http://pic.dhe.ibm.com/infocenter/lnxinfo/v3r0m0/index.jsp?topic=%2Fliaat%2Fliaatbpkvmguestca ...

随机推荐

  1. 使用匿名内部类调用start方法

    package chapter03;//类实现接口public class WD implements Runnable{//重写接口的方法 @Override public void run() { ...

  2. HDU2873 Bomb Game(二维SG函数)

    Bomb Game Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total ...

  3. 数据流分段下载(Http之 Range)

    public FileStreamResult StreamUploadedSongs(int id) { byte[] song = db.UploadedSongs.Where(x => x ...

  4. jQuery动态设置样式(style、css)

    一.jQuery设置css样式 <div style="background-color:#ffffff;padding-left:10px;">测试jQuery动态获 ...

  5. Strom的集群停止以及启动

    一:停止 1.概述 关于strom没有停止命令 2.第一种方式(kill) jps之后 使用bin/strom -kill wordcount 3.第二种方式(书写脚本) 4.先新建superviso ...

  6. oracle中tables和views的区别

    tables存储的行和列的数据,可以做任何操作 views存储的是算法,是虚拟的数据

  7. codeforces 366C Dima and Salad 【限制性01背包】

    <题目链接> 题目大意: 在一个水果篮里有n种水果,并且这些水果每一种都有一个美味度和一个卡路里的属性, 小明要从这些水果中选出来一些做一个水果沙拉, 并且要求他的水果沙拉的美味度是卡路里 ...

  8. P3917 异或序列

    P3917 异或序列暴力前缀异或枚举每一个区间,再求和,60分.正解:按每一位来做对于区间[l,r],如果它对答案有贡献,区间中1的个数一定是奇数,可以按每一位取(1<<i)的前缀和,q[ ...

  9. Anchor 的两种编程实现

    aspect ratios:高宽比率 假设 window 的尺寸为:\((w, h)\),锚框的尺寸为:\((w_1, h_1)\),则有: \[ \begin{cases} \frac{w_1h_1 ...

  10. java enum的一种写法记录

    public enum TestEnum { provider { @Override public void provide() { this.name = "hjzgg"; } ...