声明:
本文只作为参考,我也是初学者,请辩证的看待文章内容,欢迎各位在评论区补充经典博文链接。
本篇文章主要记录下文件系统中 读写文件 相关的内容
读写文件就是文件IO,文件input和output
整体结构如下:
用户态buffer
————————————————————
libc buffer
————————————————————
page cache
————————————————————
IO Queue+ device driver
—————————————————————
disk cache
——————————————————————
disk
首先是用户要写的内容,当然是放到自己分配的空间(用户态buffer),接下来就是如果使用了fwrite函数(libc提供)那么就要用到libc的buffer,libc下面是VFS提供的write系统调用,用到了pagecache,pagecache就到了内核这块了,接下来到设备的cache,最后存储到物理设备。
如果想绕过libc的buffer,就不用libc提供的文件操作函数,比如fread,fwrite等等,我个人也没有感觉到fXXX函数的优越性,所以一般不用。
不用libc了,那就直接到了VFS了,也就是直接系统调用了,系统调用就要用到pagecache了,那如果想绕过pagecache呢?
那就在open的时候加个O_DIRECT
关于O_DIRECT在man中的描述如下:
O_DIRECT (since Linux 2.4.10) Try to minimize cache effects of the I/O to and from this file. In general this will degrade performance, but it is useful in special situations, such as when applications do their own caching. File I/O is done directly to/from user- space buffers. The O_DIRECT flag on its own makes an effort to transfer data synchronously, but does not give the guarantees of the O_SYNC flag that data and necessary metadata are transferred. To guarantee synchronous I/O, O_SYNC must be used in addition to O_DIRECT. See NOTES below for further discussion. A semantically similar (but deprecated) interface for block devices is described in raw(8).
提到O_DIRECT会尽力同步写数据,也就是不一定完全按照synchronous的那种方式来,一定是写完才返回,言外之意是:没写完也会返回?那没写完的怎么处理?我不知道了。文中也建议把O_SYNC加上。
最后同时提到了O_RAW,这个查相关博客看到可以直接写扇区,man手册提到已经废弃了,不建议使用。
文件的几种访问方式:
1,以标准的系统调用或者libc系列函数来访问,也就是read write或者fread fwrite系列函数,也就是缓存io,需要说明的是linux的
缓存io其实就是 延迟写,对于系统调用来说,只是写到了pagecache,什么时候写到磁盘是内核决定的,当然也可以调用
fsync来直接写到磁盘,注意是磁盘,不是磁盘缓存。与延迟写对应的还有一个叫异步写,异步写后面说。
2,directio,就是加O_DIRECT标识,直接访问也就是没有内核的缓冲,这种方式一般情况都会造成性能下降,但是对于想自己
控制缓存的应用来说,至少是一个办法,比如有应用想自己在应用层做缓存,就没有必要再引入一层缓存了。
3,mmap
4,异步io访问
mmap
mmap就是把文件映射到内存的虚拟地址空间,实现文件磁盘地址和进程虚拟地址空间中的映射关系,像访问内存一样访问文件。但是怎么实现的?
实现具体代码没有查看过,只是网上搜集出来的实现情景:
首先找到一块连续的虚拟地址段,然后内核会针对mmap分配的虚拟内存地址段建立一个vma,vma对于可执行文件的各个段都有一个对应的结构,vma里面会定义针对映射的这个段的函数操作集合,功能类比read,write,但是操作每个段肯定不一样,接着通过fd找到文件信息,在读内存的时候,就会触发缺页中断,进而使用vma里面的函数读文件,并且拷贝到用户内存空间,也就是说略过了内核的pagecache。少了一次拷贝,这就是mmap的优势,呵呵,欢迎各位补充。
异步io
对于linux主要有两个版本的异步io可以使用
glibc的异步io 和linux本身的libaio
先说glibc,glibc是用户态的异步io,主要实现方式就是多线程,提交一个读写请求,glibc会动态的创建线程来同步的执行读写操作,执行完了通过回调或者其它方式通知用户。对的,就是通过多线程来做的!
接下来是libaio
libaio是针对regular file的,对于socket用不了!!
对于常规文件,在读文件的时候,虽然我们感觉时间很短,但是其实也是阻塞的,读到数据成功为止,比如read一个文件,返回的时候要么读到数据,要么失败,读到数据就是拷贝到了用户态的缓冲区间,经过了磁盘到内核到用户态这么一个过程。使用libaio可以直接下发一个read指令,然后就返回了,内核会把磁盘数据拷贝到用户态空间,应该略过了内核缓冲?。至于什么原理,我讲不出个所以然,查了一些文档也没有讲个什么,后续更新吧。