buffer & cache
- buffer(缓冲区)
主要目的是进行流量整形,把突发的大数量较小规模的 I/O 整理成平稳的小数量较大规模的 I/O,以减少响应次数
(比如从网上下电影,你不能下一点点数据就写一下硬盘,而是积攒一定量的数据以后一整块一起写,不然硬盘都要被你玩坏了)。
- cache(缓存)
是为了弥补高速设备和低速设备的鸿沟而引入的中间层,最终起到加快访问速度
的作用。
page cache
内核会为每个文件单独维护一个page cache,用户进程对于文件的大多数读写操作会直接作用到page cache上,内核会选择在适当的时候将page cache中的内容写到磁盘上
(当然我们可以手工fsync
控制回写),这样可以大大减少磁盘的访问次数,从而提高性能。
page cache是linux内核文件访问过程中很重要的数据结构,page cache中会保存用户进程访问过得该文件的内容,这些内容以页为单位保存在内存中,当用户需要访问文件中的某个偏移量上的数据时,内核会以偏移量为索引,找到相应的内存页,如果该页没有读入内存,则需要访问磁盘读取数据。为了提高页得查询速度同时节省page cache数据结构占用的内存,linux内核使用树来保存page cache中的页。
page cache刷盘
脏页有两种刷盘的方式:
- 异步刷盘,脏页太多或者存在太久都会导致page cache回写磁盘,Linux中可以通过 sysctl -a | grep dirty 查看相关的控制参数;
- 调用fsync、msync等。
mmap & read/write
在了解了以上的基础之后,我们就来比较一下mmap和read/write的区别,先说一下read/write系统调用,read/write
系统调用会有以下的操作:
- 访问文件,这涉及到用户态到内核态的转换;
- 读取硬盘文件中的对应数据,内核会采用预读的方式,比如我们需要访问100字节,内核实际会将按照4KB(内存页的大小)存储在page cache中;
- 将read中需要的数据,从page cache中拷贝到用户缓冲区中。
整个过程还是比较艰辛的,基本上涉及到用户内核态的切换,还有就是数据拷贝。
接下来继续说mmap吧,mmap系统调用是将硬盘文件映射到用内存中,说的底层一些是将page cache中的页直接映射到用户进程地址空间中,从而进程可以直接访问自身地址空间的虚拟地址来访问page cache中的页,这样会并涉及page cache到用户缓冲区之间的拷贝,mmap
系统调用与read/write调用的区别在于:
- mmap只需要一次系统调用,后续操作不需要系统调用
- 访问的数据不需要在page cache和用户缓冲区之间拷贝
从上所述,当频繁对一个文件进行读取操作时,mmap会比read高效一些。