free命令显示的buffers与cached的区别

据说很少有人能说清楚 free 命令所显示的 “buffers” 与 “cached” 之间的区别:

# free
            total      used      free    shared    buffers    cached
Mem:      3848656    2983016    865640      5312    324432    2024904
-/+buffers/cache:    633680    3214976
Swap:      2031612          0    2031612

我们先列出结论,如果你对研究过程感兴趣可以继续阅读后面的段落:
“buffers” 表示块设备(block device)所占用的缓存页,包括:直接读写块设备、以及文件系统元数据(metadata)比如SuperBlock所使用的缓存页;
“cached” 表示普通文件数据所占用的缓存页。

下面是分析过程:先从用 strace 跟踪 free 命令开始,看能不能发现它是如何计算 “buffers” 和 “cached” 的:

# strace free
...
open("/proc/meminfo",O_RDONLY)        =3
lseek(3,0,SEEK_SET)                  =0
read(3,"MemTotal:        3848656 kB\nMemF"...,2047)=1170
...

显然 free 命令是从 /proc/meminfo 中读取信息的,跟我们直接读到的结果一样:

# cat /proc/meminfo
MemTotal:        3848656kB
MemFree:          865640kB
Buffers:          324432kB
Cached:          2024904kB
...
SwapTotal:      2031612kB
SwapFree:        2031612kB
...
Shmem:              5312kB
...

那么 /proc/meminfo 中的 “Buffers” 和 “Cached” 又是如何得来的呢?这回没法偷懒,只能去看源代码了。源代码文件是:fs/proc/meminfo.c ,我们感兴趣的函数是:meminfo_proc_show(),阅读得知:

“Buffers” 来自于 nr_blockdev_pages() 的返回值。
“Cached” 来自于以下公式:
global_page_state(NR_FILE_PAGES) – total_swapcache_pages – i.bufferram
以上计算cached的公式中,global_page_state(NR_FILE_PAGES) 来自 vmstat[NR_FILE_PAGES],表示所有的缓存页(page cache)的总和,它包括:

Cached
buffers
交换区缓存(swap cache)
这里简单解释一下swap cache:

那些匿名内存页,比如用户进程通过malloc()申请的内存页是没有关联任何文件的(有别于backing storage基于磁盘文件的内存页),如果发生swapping换页,这类内存页会被写入交换区。从一个匿名内存页被确定要被换页开始,它就被计入了swap cache,但是不一定会被立刻写入物理交换区,因为Linux的原则是除非绝对必要,尽量避免I/O。所以swap cache中包含的是被确定要swapping换页、但是尚未写入物理交换区的匿名内存页。

vmstat[NR_FILE_PAGES] 可以通过 /proc/vmstat 来查看,表示所有缓存页的总数量:

# cat /proc/vmstat
...
nr_file_pages587334
...

注意以上nr_file_pages是以page为单位,而不像free命令是以KB为单位,一个page等于4KB。

直接修改 nr_file_pages 的内核函数是:
__inc_zone_page_state(page, NR_FILE_PAGES) 和
__dec_zone_page_state(page, NR_FILE_PAGES),
一个用于增加,一个用于减少。

先看”cached”:
“Cached” 就是除去 “buffers” 和 “swap cache” 之外的缓存页的数量:
global_page_state(NR_FILE_PAGES) – total_swapcache_pages – i.bufferram
所以关键还是要理解 “buffers” 是什么含义。

来看看”buffers” :
从源代码中看到,”buffers” 来自于 nr_blockdev_pages() 的返回值,我们来看一下这个函数是干什么的:

longnr_blockdev_pages(void)
{
        structblock_device *bdev;
        longret=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);
        returnret;
}

这段代码很简单,意思是遍历所有的块设备(block device),累加每个块设备的inode的i_mapping的页数,统计得到的就是 buffers。所以很明显,buffers 是与块设备直接相关的。

那么谁会更新块设备的缓存页数量(nrpages)呢?我们继续向下看。

搜索kernel源代码发现,最终更新mapping->nrpages字段的函数就是add_to_page_cache和__remove_from_page_cache:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/15579.html