在现代计算机系统中,CPU,RAM,DISK的速度不相同,按速度高低排列
为:CPU>RAM>DISK。CPU与RAM之间、RAM与DISK之间的速度差异常常是指数级。同时,它们之间的处理容量也不相同,其差异也是指数级。为了在速度和容量上折中,在CPU与RAM之间使用CPU cache以提高访存速度,在RAM与磁盘之间,操作系统使用page cache提高系统对文件的访问速度。
操作系统在处理文件时,需要考虑两个问题:
1. 存放在磁盘上的文件读写速度比内存差了一个数量级。
2. 文件加载到内存一次,供多个程序共享。
例如,在Windows系统中,一个进程中常常包含各种系统DLL模块,其大约占用15MB空间,如果这些DLL不被共享,将会占用大量的系统内存。
操作系统使用page cache机制解决上面的两个问题。
以下是一个应用程序事例,在linux下, render进程对sence.dat文件的操作步骤如下:
当render程序从sence.dat中读取了12KB数据之后,系统的页面内存可能如下:
对于系统的所有文件I/O请求,操作系统都是通过page cache机制实现的,对于操作系统而言,磁盘文件都是由一系列的数据块顺序组成,数据块的大小随系统不同而不同,x86 linux系统下是4KB(一个标准页面大小)。内核在处理文件I/O请求时,首先到page cache中查找(page cache中的每一个数据块都设置了文件以及偏移信息),如果未命中,则启动磁盘I/O,将磁盘文件中的数据块加载到page cache中的一个空闲块。之后再copy到用户缓冲区中。
很明显,同一块文件数据,在内存中保存了两份,这既占用了不必要的内存空间、冗余的拷贝、以及造成的CPU cache利用率不高。针对此问题,操作系统提供了内存映射机制(linux中mmap、windows中Filemapping)。如下图:
在使用mmap调用时,系统并不是马上为其分配内存空间,而仅仅是添加一个VMA到该进程中,当程序访问到目标空间时,产生缺页中断。在缺页中断中,从pagecaches中查找要访问的文件块,若未命中,则启动磁盘I/O从磁盘中加载到pagecaches。然后将文件块在pagecaches中的物理页映射到进程mmap地址空间。
当程序退出或关闭文件时,系统是否会马上清除page caches中的相应页面呢?答案是否定的。由于该文件可能被其他进程访问,或该进程一段时间后会重新访问,因此,在物理内存足够的情况下,系统总是将其保持在page caches中,这样可以提高系统的整体性能(提高page caches的命中率,尽量少的访问磁盘)。只有当系统物理内存不足时,内核才会主动清理page caches。
当进程调用write修改文件时,由于page cache的存在,修改并不是马上更新到磁盘,而只是暂时更新到page caches中,同时mark 目标page为dirty,当内核主动释放pagecaches时,才将更新写入磁盘(主动调用sync时,也会更新到磁盘)。
Filemapping可以是共享或私有的。对于共享的Filemapping,情况较简单。多个进程mmap映射到相同的pagecache。
当filemapping为私有的时,为了防止一个进程修改操作影响到另一个进程,处理情形如下图: