write和fwrite
如果只是普通地以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相关推荐
- 33. 使用fread()/fwrite()往文件中写入结构体,从文件中读出结构体
1 //读写结构体 2 #include <stdio.h> 3 typedef struct student 4 { 5 int num; 6 char name[30]; 7 char ...
- C语言 fread()与fwrite()函数说明与示例
1.作用 读写文件数据块. 2.函数原型 (1)size_t fread ( void * ptr, size_t size, size_t count, FILE * stream ); 其中,pt ...
- fwrite ,fprintf的作用与区别
scanf和fpintf是一对,用fprintf写的必须用fscanf来读. fread和fwrite是一对,用fwrite写的必须用fread来读. 同样的数据,使用fprintf和fwrite写下 ...
- 写文件 —— 将内容按照指定格式写入配置文件(fwrite()函数-》》向指定的文件中写入若干数据块)
例如 -- 文件中的配置内容格式如下: dat.txt的文件的内容为 [root@localhost tool]# cat dat.txt aa1213bbcc1415dd 参数说明 size_t ...
- 【C 语言】文件操作 ( 按照内存块的方式读写文件 | fread 函数 | fwrite 函数 )
文章目录 一.fwrite 函数 二.fread 函数 三.按照内存块的方式写文件 四.按照内存块的方式读文件 一.fwrite 函数 fwrite 函数 : 将 const void *ptr 指针 ...
- 【C 语言】文件操作 ( 使用 fread 和 fwrite 实现二进制文件的拷贝 | stat 统计文件大小 | feof 判定文件结尾 )
文章目录 一.stat 统计文件大小 二.feof 判定文件结尾 三.使用 fread 和 fwrite 实现二进制文件的拷贝 一.stat 统计文件大小 统计文件大小 , 需要借助 stat 结构体 ...
- 多进程同时写一个文件会怎样?分别用write和fwrite去观察现象
一.问题还原 在多进程的环境下,父子进程同时去写一个文件,例如父进程每次写入aaaaa,子进程每次写入bbbbb,问题是会不会出现写操作被打断的现象,比如出现aabbbaaabb这样交替的情况? 二. ...
- 写文件函数 Linux C fwrite,C文件读写函数介绍(转)
1.fopen() fopen的原型是:FILE *fopen(const char *filename,const char *mode),fopen实现三个功能:为使用而打开一个流,把一个文件和此 ...
- fwrite视频写入帧率测试(不用测了。。)
文章目录 背景 准备工作 代码 20211229 背景 我有点不明白,fwrite的写入间隔是否会影响视频的帧率,如果不影响,那最终视频的帧率是由什么决定的,我决定做一个测试,因为这对我来说确实很重要 ...
- c语言 fopen、fwrite、fread、fclose函数(打开文件进行读写覆盖或追加)
文章目录 fopen函数 描述 声明 参数 返回值 实例 fwrite函数 描述 声明 参数 返回值 实例 fread函数 描述 声明 参数 返回值 实例 fclose函数 描述 声明 参数 返回值 ...
最新文章
- 制作新版STC单片机WiFi下载器
- Bootstrap模态框使用WebUploader点击失效问题解决
- JavaScript实现递归楼梯问题(迭代解决方案)算法(附完整源码)
- 3389远程连接问题的一个解决办法
- 使用SPA/GPA 参数--SAP内存参数设置SET /GET PARAMTER ID
- Java常见排序算法之快速排序详解
- 解决同一办公环境局域网下无法添加打印机的情况
- python 期货策略_Python版商品期货跨期布林对冲策略.md
- 加菲猫台词 (请对号入座-:))
- c语言编程图形篮球,c语言程序设计 用高级语言实现篮球联赛个人技术数据处理系统...
- 流水灯verilog实验原理_6个简单的儿童科学实验,培养孩子的创造力和发散思维...
- 怎样合理有效的与人争论(讨论)问题?
- Python将numpy(.npy文件)存储为.ply文件
- CMOS器件与TTL器件 CMOS电平与TTL电平
- 不能将类型“”分配给类型“never”
- resultMap与resultType的区别
- 如何解决win11中无法打开“本地组策略编辑器”
- linux当前目录改为桌面,Latte Dock 作为一个 KDE 漂亮的桌面面板替换
- Web shell 与 冰蝎、蚁剑、哥斯拉(新手必看)
- 极米科技上市:发明专利曾被宣告无效,钟波的“5年颠覆”豪言落空