linux内核O_SYNC,文件IO - O_DIRECT和O_SYNC详解《转载》

用于传递数据的缓冲区,其内存边界必须对齐为块大小的整数倍

数据传输的开始点,即文件和设备的偏移量,必须是块大小的整数倍

待传递数据的长度必须是块大小的整数倍。

不遵守上述任一限制均将导致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状态。

函数声明:

1

2

#include

int fsync(int fd);

函数返回值:

0: success

-1: error

返回时间:仅在对磁盘设备(或者至少是其高速缓存)的传递完成后,fsync()调用才会返回。

2 fdatasync

作用:fdatasync()系统调用的作用类似fsync(),只是强制文件处于synchronized IO data

integrity compeletion状态。

函数声明:

1

2

#include

int fdatasync(int fd);

函数返回值:

0: success

-1: error

与fsync的区别:fdatasync()可能会减少磁盘操作的次数,由fsync()调用请求的两次变成一次。例如,修改了文件的数据,而文件大小不变,那么调用fdatasync调用请求只强制进行了数据更新,相比之下,fsync()调用会强制将元数据传递到磁盘上,而元数据和文件数据通常驻留在磁盘的不同区域,更新这些数据需要反复在整个磁盘上执行寻道操作。

3 sync系统调用

作用:sync()系统调用会使包含更新文件信息的所有内核缓冲区(即数据块、指针块、元数据等)刷新到磁盘上。

函数声明:

1

2

#include

void sync(void);

细节:若内容发生变化的内核缓冲区在30s内未经显式方式同步到磁盘上,则一条长期运行的内核线程会确保将其刷新到磁盘上。这一做法是为了规避缓冲区与相关磁盘文件内容长期处于不一致状态。

4 使所有写入同步:O_SYNC

调用open()函数时,如制定O_SYNC标志,则会使所有后续输出同步。

1

fd = open(pathname, O_WRONLY | O_SYNC);

作用:调用open后,每个write调用会自动将文件数据和元数据刷新到磁盘上,即按照Synchronized IO file

integrity completion的要求执行写操作。

5 有无O_SYNC性能对比

场景:将一百万字节写入一个ext2文件系统上的新创建文件,比较写入时间。

对比结果:

a4c26d1e5885305701be709a3d33442f.png

从结果中可以得到的结论:

采用O_SYNC标志(或者频繁调用fsync(), fdatasync()或sync())对性能影响极大。

性能下降的直接表现为运行总用时大为增加:在缓冲区为1字节的情况下,运行时间相差1000多倍。

以O_SYNC标志执行写操作时运行总用时和CPU时间之间的巨大差异(1030 -

98.8),原因是系统在每个缓冲区中将数据向磁盘传递时会把程序阻塞起来。

三,IO缓冲层次关系

先总结一下stdio函数库和内核采用的缓冲这两级缓冲,然后用图说明两层缓冲机制和各种缓冲类型的控制机制。

首先,通过stdio库将用户数据传递到stdio缓冲区,该缓冲区位于用户态内存区。

当缓冲区填满,stdio库会调用write()系统调用,将数据传递到内核高速缓冲区,该缓冲区位于内核态内存区。

最终,内核发起磁盘操作。

该层次结构如下图所示

a4c26d1e5885305701be709a3d33442f.png

上图中,左侧虚线方框中为可于任何时刻显式强制刷新各类缓冲区的调用。

右侧所示为促使刷新自动化的调用:通过禁用stdio的缓冲,和在文件输出类的系统调用中启用同步,从而使每个write()调用立刻刷新到磁盘。

四,小结

输入输出数据的缓冲由内核和stdio库完成。有时可能希望阻止缓冲,但这需要了解其对应用程序性能的影响。

可以使用各种系统调用和库函数来控制内核和stdio缓冲,并执行一次性的缓冲区刷新。

在Linux环境下,open()所特有的O_DIRECT标识允许特定应用跳过缓冲区高速缓存。

虽然题目还是UNIX高级环境变成(xx),但是打算把所阅读和参考的书换成《Linux/UNIX系统编程手册》。感觉这本书内容更新一点。

工作很忙,周末大部分时间都在外面活动,跑步拍照,虽然只是简单的读书这一篇也是拖了又拖才敲完。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值