如果只是普通地以O_RDWR的flag去open一个文件朝里write(不考虑创建、扩增),那默认内核会把文件的这个页面读进来缓存在内核里的,也即所谓的page cache。随后再发起新的write syscall写相同的页面时,只要写在page cache里就可以结束syscall直接返回了。

内核的这个page cache有很多好处,比如你的程序对io还没到需要自己做用户态的读写缓存,那内核的这个机制就帮你省去了很多工作,毕竟page cache是在内存里的,而且可以拿来做read hit,相比于每次read/write都要访问磁盘,带来的性能优势还是很不错的,算是惠及大部分普通程序。
fwrite是用户态的glibc库,相当于把write的系统调用封装了一下,关键一点在于,他在用户态又多加了一个buffer,只有当你的fwrite写入量够多或者你主动fflush才会真的发起一个write syscall。

所以fwrite的好处是对于小量的写,减少syscall的次数,毕竟如果你每写一个字节都要发起一个syscall,然后特权级切换到内核,这是比较耗性能的。

程序的最终目的是要把数据写到磁盘上, 但是系统从通用性和性能角度,尽量提供一个折中的方案来保证这些。让我们来看一个最常用的写文件典型example,也是路径最长的IO。

{char *buf = malloc(MAX_BUF_SIZE);strncpy(buf, src, , MAX_BUF_SIZE);fwrite(buf, MAX_BUF_SIZE, 1, fp);fclose(fp);}

这里malloc的buf对于图层中的application buffer,即应用程序的buffer;调用fwrite后,把数据从application buffer 拷贝到了 CLib buffer,即C库标准IObuffer。fwrite返回后,数据还在CLib buffer,如果这时候进程core掉。这些数据会丢失。没有写到磁盘介质上。当调用fclose的时候,fclose调用会把这些数据刷新到磁盘介质上。除了fclose方法外,还有一个主动刷新操作fflush 函数,不过fflush函数只是把数据从CLib buffer 拷贝到page cache 中,并没有刷新到磁盘上,从page cache刷新到磁盘上可以通过调用fsync函数完成。

有人说,我不想通过fwrite+fflush这样组合,我想直接写到page cache。这就是我们常见的文件IO调用read/write函数。这些函数基本上是一个函数对应着一个系统调用,如sys_read/sys_write。调用write函数,是直接通过系统调用把数据从应用层拷贝到内核层,从application buffer 拷贝到 page cache 中。

最后是O_DIRECT,如果你在open一个文件的时候加上这个flag,那么后续对这个文件的所有read/write syscall都会bypass掉内核的page cache。也就是read/write直接发起disk io,不会在内核中缓存。

到这你会发现,O_DIRECT相当于把前面提到的各种buffer全扔了,直进直出,不存在什么locality、复用。
buffer,或者缓存虽然有好处,但也有适用条件以及额外的开销:

  • 如果你的程序的文件读写几乎没有locality或者什么热点,加缓存不会带来cache hit方面的性能提升。
  • 反而如果你写入的数据量很大,那用fwrite时,会发生你程序的buffer到glibc buffer一次拷贝。glibc buffer发起write syscall到page cache一次拷贝;而如果是普通write或O_DIRECT的write),只会发生你程序的buffer到page cache(disk的buffer cache)一次拷贝。
    -注意:2.4之后的内核page cache和disk buffer cache合并,所以page cache的内容直接可以移交给disk buffer cache用于写回
    所以从性能的角度,fwrite并不适合大量写的场景。然而光从这个角度并不能看出O_DIRECT的有无带来什么作用。O_DIRECT适用于,数据读写性能、一致性、locality、写回时机等等对你的程序已经重要到全都要你自己管理,这时内核自带的page cache那种粗粒度、不太可控的设施已经不能满足你的需求了。

使用直接IO需要遵守的一些限制:

  • 用于传递数据的缓冲区,其内存边界必须对齐为块大小的整数倍。
  • 数据传输的开始点,即文件和设备的偏移量,必须是块大小的整数倍
  • 待传递数据的长度必须是块大小的整数倍。

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

最典型的就是数据库应用:
对数据库而言,依赖于page cache会带来非常多问题,比如:

  • writeback,也即写回磁盘同步的时机不可控。page cache可能在任何时候写回,包括你的事务做到一半,进程遭到调度,内核擅自把部分page cache上的内容写回磁盘,造成预期外的数据不一致。
    那么此时最好的解决方案是,所有对文件内容的cache,自己在用户态管理,bypas掉内核的page cache,而这就要借助O_DIRECT。什么时候写回,写回哪些,自己说了算。

最后提一下O_SYNC的问题,因为往往会和O_DIRECT一起用。从数据库的例子里可以看出,O_DIRECT既然控制写回,就和事务、数据一致性脱不了关系。一致性很重要的一个问题就是事情的发生顺序:写回真的做完了吗?
单靠O_DIRECT并不能保证写回的“完成”,因为O_DIRECT只是bypass掉page cache,保证数据直接提交到disk buffer cache,然而此时write即便返回,也不能保证disk buffer cache上的数据真的进入存储介质中(比如block layer的调度)。也即数据仍然在内存中,我们怎么才能保证write返回时数据就已经进入存储介质呢?答案就是O_SYNC。
所以O_DIRECT和O_SYNC一起使用,可以确保每一次写返回时,数据以bypass page cache的方式写,且已经进入存储介质。

write和fwrite相关推荐

  1. 33. 使用fread()/fwrite()往文件中写入结构体,从文件中读出结构体

    1 //读写结构体 2 #include <stdio.h> 3 typedef struct student 4 { 5 int num; 6 char name[30]; 7 char ...

  2. C语言 fread()与fwrite()函数说明与示例

    1.作用 读写文件数据块. 2.函数原型 (1)size_t fread ( void * ptr, size_t size, size_t count, FILE * stream ); 其中,pt ...

  3. fwrite ,fprintf的作用与区别

    scanf和fpintf是一对,用fprintf写的必须用fscanf来读. fread和fwrite是一对,用fwrite写的必须用fread来读. 同样的数据,使用fprintf和fwrite写下 ...

  4. 写文件 —— 将内容按照指定格式写入配置文件(fwrite()函数-》》向指定的文件中写入若干数据块)

    例如 -- 文件中的配置内容格式如下: dat.txt的文件的内容为 [root@localhost tool]# cat dat.txt  aa1213bbcc1415dd 参数说明 size_t ...

  5. 【C 语言】文件操作 ( 按照内存块的方式读写文件 | fread 函数 | fwrite 函数 )

    文章目录 一.fwrite 函数 二.fread 函数 三.按照内存块的方式写文件 四.按照内存块的方式读文件 一.fwrite 函数 fwrite 函数 : 将 const void *ptr 指针 ...

  6. 【C 语言】文件操作 ( 使用 fread 和 fwrite 实现二进制文件的拷贝 | stat 统计文件大小 | feof 判定文件结尾 )

    文章目录 一.stat 统计文件大小 二.feof 判定文件结尾 三.使用 fread 和 fwrite 实现二进制文件的拷贝 一.stat 统计文件大小 统计文件大小 , 需要借助 stat 结构体 ...

  7. 多进程同时写一个文件会怎样?分别用write和fwrite去观察现象

    一.问题还原 在多进程的环境下,父子进程同时去写一个文件,例如父进程每次写入aaaaa,子进程每次写入bbbbb,问题是会不会出现写操作被打断的现象,比如出现aabbbaaabb这样交替的情况? 二. ...

  8. 写文件函数 Linux C fwrite,C文件读写函数介绍(转)

    1.fopen() fopen的原型是:FILE *fopen(const char *filename,const char *mode),fopen实现三个功能:为使用而打开一个流,把一个文件和此 ...

  9. fwrite视频写入帧率测试(不用测了。。)

    文章目录 背景 准备工作 代码 20211229 背景 我有点不明白,fwrite的写入间隔是否会影响视频的帧率,如果不影响,那最终视频的帧率是由什么决定的,我决定做一个测试,因为这对我来说确实很重要 ...

  10. c语言 fopen、fwrite、fread、fclose函数(打开文件进行读写覆盖或追加)

    文章目录 fopen函数 描述 声明 参数 返回值 实例 fwrite函数 描述 声明 参数 返回值 实例 fread函数 描述 声明 参数 返回值 实例 fclose函数 描述 声明 参数 返回值 ...

最新文章

  1. 制作新版STC单片机WiFi下载器
  2. Bootstrap模态框使用WebUploader点击失效问题解决
  3. JavaScript实现递归楼梯问题(迭代解决方案)算法(附完整源码)
  4. 3389远程连接问题的一个解决办法
  5. 使用SPA/GPA 参数--SAP内存参数设置SET /GET PARAMTER ID
  6. Java常见排序算法之快速排序详解
  7. 解决同一办公环境局域网下无法添加打印机的情况
  8. python 期货策略_Python版商品期货跨期布林对冲策略.md
  9. 加菲猫台词 (请对号入座-:))
  10. c语言编程图形篮球,c语言程序设计 用高级语言实现篮球联赛个人技术数据处理系统...
  11. 流水灯verilog实验原理_6个简单的儿童科学实验,培养孩子的创造力和发散思维...
  12. 怎样合理有效的与人争论(讨论)问题?
  13. Python将numpy(.npy文件)存储为.ply文件
  14. CMOS器件与TTL器件 CMOS电平与TTL电平
  15. 不能将类型“”分配给类型“never”
  16. resultMap与resultType的区别
  17. 如何解决win11中无法打开“本地组策略编辑器”
  18. linux当前目录改为桌面,Latte Dock 作为一个 KDE 漂亮的桌面面板替换
  19. Web shell 与 冰蝎、蚁剑、哥斯拉(新手必看)
  20. 极米科技上市:发明专利曾被宣告无效,钟波的“5年颠覆”豪言落空

热门文章

  1. 罗永浩疑回应再被强制执行
  2. element UI 修改 table 中某一列的值
  3. Linux 参数之 max_map_count
  4. IT运维人员,该如何规划自己的职业路?
  5. c++ 构造函数 which is of non-class type奇葩问题
  6. NRF52832+DW1000通信系统架构设计
  7. 必备模块知识——超声波传感器
  8. 脑电波技术的前途光明吗?
  9. Matlab标题加变量
  10. 在微控制器平台等小型物联网设备上运行 JavaScript