什么是PageCache?为什么需要PageCache?
应用程序产生PageCache的逻辑示意图
红色的地方就是PageCache,很明显,PageCache是内核管理的内存,也就是说,它属于内核不属于用户。
如何观察PageCache?在Linux上直接查看PageCache的方式有很多,包括procmeminfo、free、procvmstat命令等,它们的内容其实是一致的。拿procmeminfo命令举例看一下:
catprocmeminfo
。。。
Buffers:1224kB
Cached:111472kB
SwapCached:36364kB
Active:6224232kB
Inactive:979432kB
Active(anon):6173036kB
Inactive(anon):927932kB
Active(file):51196kB
Inactive(file):51500kB
。。。
Shmem:10000kB
。。。
SReclaimable:43532kB
根据上面的数据,你可以简单得出这样的公式(等式两边之和都是112696KB):
BuffersCachedSwapCachedActive(file)Inactive(file)ShmemSwapCached
那么等式两边的内容就是我们平时说的PageCache。请注意你没有看错,两边都有SwapCached,之所以要把它放在等式里,就是说它也是PageCache的一部分。等式右边这些项把Buffers和Cached做了一下细分,分为了Active(file),Inactive(file)和Shmem,因为Buffers更加依赖于内核实现,在不同内核版本中它的含义可能有些不一致,而等式右边和应用程序的关系更加直接,所以我们从等式右边来分析。
在PageCache中,Active(file)Inactive(file)是Filebackedpage(与文件对应的内存页),是你最需要关注的部分。因为你平时用的mmap()内存映射方式和bufferedIO来消耗的内存就属于这部分,最重要的是,这部分在真实的生产环境上也最容易产生问题,我们在接下来的课程案例篇会重点分析它。
而SwapCached是在打开了Swap分区后,把Inactive(anon)Active(anon)这两项里的匿名页给交换到磁盘(swapout),然后再读入到内存(swapin)后分配的内存。由于读入到内存后原来的SwapFile还在,所以SwapCached也可以认为是Filebackedpage,即属于PageCache。这样做的目的也是为了减少IO。
通过下面简单的示意图明白SwapCached是怎么产生的:
SwapCached只在Swap分区打开的情况下才会有,而我建议你在生产环境中关闭Swap分区,因为Swap过程产生的IO会很容易引起性能抖动。
PageCache中的Shmem是指匿名共享映射这种方式分配的内存(free命令中shared这一项),比如tmpfs(临时文件系统),这部分在真实的生产环境中产生的问题比较少。
free命令中的buffcache究竟是指什么呢?
free命令也是通过解析procmeminfo得出这些统计数据的,这些都可以通过free工具的源码来找到。free命令的源码是开源,你可以去看下procfs里的free。c文件,源码是最直接的理解方式,它会加深你对free命令的理解。
freek
totalusedfreesharedbuffcacheavailabl
Mem:792658072779604923921000015622843068
Swap:82247643807487844016
通过procfs源码里面的procsysinfo。c这个文件,你可以发现buffcache包括下面这几项:
buffcacheBuffersCachedSReclaimable
通过前面的数据我们也可以验证这个公式:122411147243532的和是156228。
free命令中的buffcache是由Buffers、Cached和SReclaimable这三项组成的,它强调的是内存的可回收性,也就是说,可以被回收的内存会统计在这一项。
SReclaimable是指可以被回收的内核内存,包括dentry和inode等。
掌握了PageCache具体由哪些部分构成之后,在它引发一些问题时,你就能够知道需要去观察什么。比如说,应用本身消耗内存(RSS)不多的情况下,整个系统的内存使用率还是很高,那不妨去排查下是不是Shmem(共享内存)消耗了太多内存导致的。
为什么需要PageCache?
第一张图你其实已经可以直观地看到,标准IO和内存映射会先把数据写入到PageCache,这样做会通过减少IO次数来提升读写效率。
看一个具体的例子。首先,我们来生成一个1G大小的新文件,然后把PageCache清空,确保文件内容不在内存中,以此来比较第一次读文件和第二次读文件耗时的差异。具体的流程如下。先生成一个1G的文件:
ddifdevzeroofhometestdd。outbs4096count((1024256))
2。清空PageCache,需要先执行一下sync来将脏页同步到磁盘再去dropcache。
syncecho3procsysvmdropcaches
3。第一次读取文件的耗时如下:
timecathometestdd。outdevnull
real0m5。733s
user0m0。003s
sys0m0。213s
再次读取文件的耗时如下:
timecathometestdd。outdevnull
real0m0。132s
user0m0。001s
sys0m0。130s
第二次读取文件的耗时远小于第一次的耗时,这是因为第一次是从磁盘来读取的内容,磁盘IO是比较耗时的,而第二次读取的时候由于文件内容已经在第一次读取时被读到内存了,所以是直接从内存读取的数据,内存相比磁盘速度是快很多的。这就是PageCache存在的意义:减少IO,提升应用的IO速度。