fopen和Open,read和fread,write和fwrite有什么区别,很多人都会弄混了,而这经常会带来一些问题。所以在这里理清他们的关系是很有必要的。
open/read/write是Linux提供的系统调用,用户态的程序只能通过这些接口来访问文件系统层。而fopen/fread/fwrite是C库提供的文件读写接口,其核心实现是基于open/read/write这一类的系统调用。说到这有些人可能会说,那我再写代码的时候应该用哪一套?其实都是可以的,主要看你的需求,C库函数的目的是为了方便编程,所以C库的文件接口挺了一些额外的处理,例如字符串和文本的处理,比如fputs和fgets,所以如果你在做文本数据的输入输出,无疑C库会是更好的选择。
Note:绝大部分的C库都为文件接口提供一层缓存,所以你调用fwrite操作的时候,实际上数据是先放到这一层缓存的,在编程的时候必须注意。
Linux数据回写
这一部分是问的最多的问题,很多对刚接触文件接口(包括前面的系统调用和C库函数)的人都会觉得很奇怪:为什么我fwrite/write函数已经返回了,此时掉电或重启后数据回丢失?问题的根源在于缓存的存在,由于存储设备属于低速设备,直接操作的话会有严重的延迟,所以通常会在DRAM上先缓存一部分数据。而DRAM是易失性存储设备,掉电数据就丢了,所以要确保数据固化就要把数据会写到存储设备上。
数据回写有两种方式:1,主动同步回写;2,异步后台回写;在了解数据如何回写前,有一点必须要要注意,你的缓存有可能有好多层,所以你必须从上到下把每一层的缓存都刷出去(即写到下一层)。这一点我们下面会举几个具体的例子,先介绍怎么主动同步回写。
先看一个系统调用的例子:
write只是把数据提交到内核的Page Cache(假设没有启用DIO),要想写到存储设备,必须通过fdatasync或fsync系统调用。下面看一些修正后的代码:
看完系统调用的例子,再来看一下C库的例子:
我们知道C库也有一层文件缓存,所以掉电是因为数据还在这一层缓存中,熟悉C库的同学可能知道这里要加一个fflush,其实并不对,fflush只能保证把缓存回写到下一层,即内核的Page Cache,依然需要调用fsync再刷到物理存储设备。正确的写法如下: