在前几篇中我们介绍了磁盘、文件系统、inode、硬链接、软链接、文件描述符,没看过的同学可以先看一下前几篇文章。
由于硬件的限制,如果cpu直接访问磁盘进行文件读写的话,传输速度会很慢,效率很低。所以linux在内存中对访问磁盘文件的内容进行了缓存,通过尽量减少对磁盘的I/O操作,从而提高性能。这种用来缓存磁盘文件内容的内存就是page cache。
page cache以页为单位,一个page cache页一般为4KB,也有16 ~ 64KB的情况。page cache只会缓存文件的部分内容,只需要把当前真正被访问到的文件数据部分缓存起来就可以了,不需要把整个文件都缓存到内存中。
PageCache读写
当用户进程调用read()系统调用读取某个文件时,内核先查看page cache里是否有读取的文件内容,如果有就直接从page cache中读取具体信息。如果page cache里没有,就启动磁盘的I/O从磁盘中读取要访问部分的文件内容放到page cache中,再从page cache中读取文件内容。如果再有访问这部分文件内容的需求,就直接从当前的page cache中读取就可以了。
如果看过上一篇文件描述符的话,可以很容易地理解下面这张图片,当有多个进程访问同一个文件内容的时候,每个进程通过一个文件描述符访问page cache,用文件描述符中的seek记录访问数据的偏移量。
当用户进程调用wirte()系统调用发起写请求时,同样也是先向page cache中写入修改的内容,然后将修改过的page cache标记为dirty。标注为dirty的page cache会加入到一个dirty list中。内核会周期性地将dirty list中的page cache写回到磁盘中,完成文件内容的落盘。如果在dirty的page cache写回磁盘之前机器发生断电关机,这部分未写回磁盘的数据将会丢失。
dirty的page cache在何时被写回磁盘,一共有这几个配置文件。
vm.dirty_background_bytes = 0
vm.dirty_background_ratio = 10
vm.dirty_bytes = 0
vm.dirty_expire_centisecs = 3000
vm.dirty_ratio = 30
vm.dirty_writeback_centisecs = 500
-
dirty_background_bytes:控制dirty的page cache页内存大小,超过dirty_background_bytes的值时,内核的flush线程开始将脏页内存写回磁盘中。
-
dirty_background_ratio:控制脏页内存占可用内存(空闲+可回收)的百分比,达到dirty_background_ratio时,内核的flush线程开始将脏页内存写回磁盘中。默认值是10,例如系统可用内存为1GB,当脏页占用100M内存时,开始写回磁盘。
-
dirty_bytes和dirty_background_bytes类似,只不过是执行磁盘写操作的进程开始执行脏页写回磁盘的任务,并非通过其他线程。
-
dirty_ratio和dirty_background_ratio类似,只不过是执行磁盘写操作的进程开始执行脏页写回磁盘的任务,并非通过其他线程。默认值是20
-
vm.dirty_expire_centisecs:数据保存于文件系统的内存中,多长时间后就会被认定为脏页。默认值为 3000,代表30 秒
-
vm.dirty_writeback_centisecs:表示每间隔多长时间,系统自动触发将脏页数据写回磁盘中。默认值为 500,代表是 5 秒
PageCache回收
Page cache另一个重要的问题就是何时释放内存空间,毕竟内存不能无限使用下去。
查看dirty page cache 数量
cat /proc/vmstat | egrep "dirty|writeback"
-
nr_dirty表示当前系统积压了多少脏页,单位是Page(4KB)
-
nr_writeback则表示有多少脏页正在回写到磁盘,单位是Page(4KB)
查看系统内存,其中buff/cache中的这些就是page cache内存的使用量。
当系统free内存不够时,会释放page cache部分的内存。
Linux给page cache维护了两个list,active list和inactive list。在active list中的page cache表示正在使用的page cache,其中的页不会被回收,例如标记为dirty的page cache不能回收,这些脏页中的数据还没有写回到磁盘中,显然不能回收。
只有在inactive list中的page cache可以被释放。第一次缓存的文件数据的内存页会加入到inactive list中,如果再次访问该内存页,则会将这个neic页从inactive list移到active list中。
这两个list类似一个先进先出的队列,当需要释放page cache的内存时,会从头部移除inactive list中的page cache。如果active list中page的数量远大于inactive list,那么active list头部的页面会被移入inactive list中,进而被系统回收。