O_DIRECT和O_SYNC是系统调用open的flag参数。经过指定open的flag参数,以特定的文件描述符打开某一文件。html
这两个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 == -1)
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,以同步方式写入文件
功能:强制刷新内核缓冲区到输出文件。这是有必要的,由于为了数据安全,须要确保将数据真正写入磁盘或者磁盘的硬件告诉缓存中。async
咱们先熟悉一下同步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);
函数返回值:
返回时间:仅在对磁盘设备(或者至少是其高速缓存)的传递完成后,fsync()调用才会返回。
2 fdatasync
做用:fdatasync()系统调用的做用相似fsync(),只是强制文件处于synchronized IO data integrity compeletion状态。
函数声明:
#include
int fdatasync(int fd);
函数返回值:
与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标识容许特定应用跳过缓冲区高速缓存。