春天来了,除了工作学习,大家也要注意锻炼身体,多出去运动运动。 

上周末在元大都遗址公园海棠花溪拍的海棠花。

 

进入正题。

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 == -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,以同步方式写入文件

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

我们先熟悉一下同步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标识允许特定应用跳过缓冲区高速缓存。
 
 

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

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

 

参考:

《Linux/UNIX系统编程手册(上册)》  

UNIX高级环境编程(14)文件IO - O_DIRECT和O_SYNC详解 < 海棠花溪 >的更多相关文章

  1. UNIX高级环境编程1

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

  2. Unix环境编程之文件IO

    1.文件IO 2.文件与目录 3.进程 4.多线程编程 5.信号 6.进程间通信 学习linux编程,首先要学会使用shell,这里一些基础命令就不介绍了.这里唯一要提的一个shell命令就是man. ...

  3. UNIX高级环境编程(10)进程控制(Process Control)- 竞态条件,exec函数,解释器文件和system函数

    本篇主要介绍一下几个内容: 竞态条件(race condition) exec系函数 解释器文件    1 竞态条件(Race Condition) 竞态条件:当多个进程共同操作一个数据,并且结果依赖 ...

  4. Unix高级环境编程

    [07] Unix进程环境==================================1. 进程终止    atexit()函数注册终止处理程序.    exit()或return语句:    ...

  5. UNIX高级环境编程(2)FIle I/O - 原子操作、共享文件描述符和I/O控制函数

    引言: 本篇通过对open函数的讨论,引入原子操作,多进程通信(共享文件描述符)和内核相关的数据结构. 还会讨论集中常见的文件IO控制函数,包括: dup和dup2 sync,fsync和fdatas ...

  6. UNIX高级环境编程(1)File I/O

    引言: Unix系统中主要的文件操作包括: open read write lseek close unbuffered IO和standard I/O相对应,后面的章节我们会讨论这两者的区别. 在讨 ...

  7. UNIX高级环境编程(3)Files And Directories - stat函数,文件类型,和各种ID

    在前面的两篇,我们了解了IO操作的一些基本操作函数,包括open.read和write. 在本篇我们来学习一下文件系统的其他特性和一个文件的属性,涉及的函数功能包括: 查看文件的所有属性: 改变文件所 ...

  8. UNIX高级环境编程(7)标准IO函数库 - 二进制文件IO,流定位,创建临时文件和内存流

    1 二进制IO(Binary IO) 在前一篇我们了解了逐字符读写和逐行读写函数. 如果我们在读写二进制文件,希望以此读写整个文件内容,这两个函数虽然可以实现,但是明显会很麻烦且多次循环明显效率很低. ...

  9. UNIX高级环境编程(6)标准IO函数库 - 流的概念和操作

    标准IO函数库隐藏了buffer大小和分配的细节,使得我们可以不用关心预分配的内存大小是否正确的问题. 虽然这使得这个函数库很容易用,但是如果我们对函数的原理不熟悉的话,也容易遇到很多问题.   1 ...

随机推荐

  1. python三大神器之pip

    pip是一款管理python各类包和库的工具,非常好用.下文介绍常用的一些命令. ● 安装:pip install 库名 也可以指定版本:pip install 库名=版本 ● 卸载:pip unin ...

  2. 消息中间件RabbitMQ(一)

    1.消息中间件 消息队列中间件是指利用高效可靠地消息传递机制传递消息.有两种传递模式:点对点模式.发布/订阅模式.流行的消息中间件有RabblitMQ.Kafka.RockerMQ.它们都提供了基于存 ...

  3. 翻译:TRUNCATE TABLE(已提交到MariaDB官方手册)

    本文为mariadb官方手册:TRUNCATE TABLE的译文. 原文:https://mariadb.com/kb/en/truncate-table/我提交到MariaDB官方手册的译文:htt ...

  4. vim之添加多行和删除多行

    1.复制单行和多行. 1)单行复制 在命令模式下,将光标移到将要复制的行处,按“yy”进行复制,按“p”进行粘贴. 2)多行复制 在命令模式下,将光标移到将要复制的行处,按“nyy”进行复制(n代表行 ...

  5. Java并发(二)—— 并发编程的挑战 与 并发机制的底层原理

    单核处理器也可以支持多线程,因为CPU是通过时间片分配算法来循环执行任务 多线程一定比单线程快么?不一定,因为线程创建和上下文切换都需要开销. 如何减少上下文切换 无锁并发编程 CAS算法 使用最少线 ...

  6. TypeScript学习之WebStorm(1)

    修改快捷键 本没有想要修改快捷键,调试的时候下意识的 按F10,发现还是习惯使用visual studio的快捷键.WebStorm支持修改快捷键,File=>Settings=>Keym ...

  7. Packet for query is too large (12238 > 1024). You can change this value

    MySQL max_allowed_packet 设置过小导致记录写入失败 mysql根据配置文件会限制server接受的数据包大小. 有时候大的插入和更新会受max_allowed_packet 参 ...

  8. 蓝桥杯试题----- 打印大X

    打印大X 小明希望用星号拼凑,打印出一个大X,他要求能够控制笔画的宽度和整个字的高度.为了便于比对空格,所有的空白位置都以句点符来代替. 要求输入两个整数m n,表示笔的宽度,X的高度.用空格分开(0 ...

  9. 1. volatale 关键字 -内存可见性

    package com.gf.demo01; /** * 一.volatile 关键字:但多个线程进行操作共享数据时,可以保证内存中数据可见性. * */ public class TestVolat ...

  10. 快速掌握JavaScript面试基础知识(二)

    译者按: 总结了大量JavaScript基本知识点,很有用! 原文: The Definitive JavaScript Handbook for your next developer interv ...