Linux的Cache和Buffer理解

首先说明 , 本文讨论的cache指的是linux中的page cache , buffer指的是buffer cache , 也即cat /proc/meminfo中显示的cache和buffer 。
我们知道 , Linux下频繁存取文件或单个大文件时物理内存会很快被用光 , 当程序结束后内存不会被正常释放而是一直作为cahce占着内存 。因此系统经常会因为这点导致OOM产生 , 尤其在等大压力场景下概率较高 , 此时 , 第一时间查看cache和buffer内存是非常高的 。此类问题目前尚未有一个很好的解决方案 , 以往遇到大多会做规避处理 , 因此本案尝试给出一个分析和解决的思路 。
解决该问题的关键是理解什么是cache和buffer , 什么时候消耗在哪里以及如何控制cache和buffer , 所以本问主要围绕这几点展开 。整个讨论过程尽量先从内核源码分析入手 , 然后提炼App相关接口并进行实际操作验证 , 最后总结给出应用程序的编程建议 。
可以通过free或者cat /proc/meminfo查看到系统的buffer和cache情况

Linux的Cache和Buffer理解

文章插图
free命令的全解析
 
1.1 Cache和Buffer分析从cat /proc/meminfo入手 , 先看看该接口的实现:
static int meminfo_proc_show(struct seq_file *m, void *v){……cached = global_page_state(NR_FILE_PAGES) - total_swapcache_pages() - i.bufferram;if (cached < 0) cached = 0;…… seq_printf(m, "MemTotal: %8lu kBn" "MemFree: %8lu kBn" "Buffers: %8lu kBn" "Cached: %8lu kBn" …… , K(i.totalram), K(i.freeram), K(i.bufferram), K(cached), …… );……}其中 , 内核中以页框为单位 , 通过宏K转化成以KB为单位输出 。这些值是通过si_meminfo来获取的:
void si_meminfo(struct sysinfo *val){ val->totalram = totalram_pages; val->sharedram = 0; val->freeram = global_page_state(NR_FREE_PAGES); val->bufferram = nr_blockdev_pages(); val->totalhigh = totalhigh_pages; val->freehigh = nr_free_highpages(); val->mem_unit = PAGE_SIZE;}其中bufferram来自于nr_blockdev_pages() , 该函数计算块设备使用的页框数 , 遍历所有块设备 , 将使用的页框数相加 。而不包含普通文件使用的页框数 。
long nr_blockdev_pages(void){ struct block_device *bdev; long ret = 0; spin_lock(&bdev_lock); list_for_each_entry(bdev, &all_bdevs, bd_list) { ret += bdev->bd_inode->i_mapping->nrpages; } spin_unlock(&bdev_lock); return ret;}从以上得出meminfo中cache和buffer的来源:
  • Buffer就是块设备占用的页框数量;
  • Cache的大小为内核总的page cache减去swap cache和块设备占用的页框数量 , 实际上cache即为普通文件的占用的page cache 。
通过内核代码分析(这里略过复杂的内核代码分析) , 虽然两者在实现上差别不是很大 , 都是通过address_space对象进行管理的 , 但是page cache是对文件数据的缓存而buffer cache是对块设备数据的缓存 。对于每个块设备都会分配一个def_blk_ops的文件操作方法 , 这是设备的操作方法 , 在每个块设备的inode(bdev伪文件系统的inode)下面会存在一个radix tree , 这个radix tree下面将会放置缓存数据的page页 。这个page的数量将会在cat /proc/meminfobuffer一栏中显示 。也就是在没有文件系统的情况下 , 采用dd等工具直接对块设备进行操作的数据会缓存到buffer cache中 。如果块设备做了文件系统 , 那么文件系统中的文件都有一个inode , 这个inode会分配ext3_ops之类的操作方法 , 这些方法是文件系统的方法 , 在这个inode下面同样存在一个radix tree , 这里也会缓存文件的page页 , 缓存页的数量在cat /proc/meminfo的cache一栏进行统计 。此时对文件操作 , 那么数据大多会缓存到page cache,不多的是文件系统文件的元数据会缓存到buffer cache 。
这里,我们使用cp命令拷贝一个50MB的文件操作,内存会发生什么变化:
[root nfs_dir] # ll -h file_50MB.bin-rw-rw-r-- 1 4104 4106 50.0M Feb 24 2016 file_50MB.bin[root nfs_dir] # cat /proc/meminfoMemTotal: 90532 kBMemFree: 65696 kBBuffers: 0 kBCached: 8148 kB……[root@test nfs_dir] # cp file_50MB.bin /[root@test nfs_dir] # cat /proc/meminfoMemTotal: 90532 kBMemFree: 13012 kBBuffers: 0 kBCached: 60488 kB可以看到cp命令前后 , MemFree从65696 kB减少为13012 kB , Cached从8148 kB增大为60488 kB , 而Buffers却不变 。那么过一段时间 , Linux会自动释放掉所用的cache内存吗?一个小时后查看proc/meminfo显示cache仍然没有变化 。


推荐阅读