良许Linux

文章数:922 被阅读:3074353

datasheet推荐 换一换
随便看看
账号入驻

【内存】buffers与cached的区别

最新更新时间:2024-10-07
    阅读数:

free 命令是Linux系统上查看内存使用状况最常用的工具,然而很少有人能说清楚 “buffers” 与 “cached” 之间的区别:

我们先抛出结论,如果你对研究过程感兴趣可以继续阅读后面的段落:

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:        3848656 kB
MemFree:          865640 kB
Buffers:          324432 kB
Cached:          2024904 kB
...
SwapTotal:       2031612 kB
SwapFree:        2031612 kB
...
Shmem:              5312 kB
...

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

Cached 来自于以下公式:

global_page_state(NR_FILE_PAGES) – total_swapcache_pages – i.bufferram

global_page_state(NR_FILE_PAGES) 表示所有的缓存页( page cache )的总和,它包括:

  • Cached
  • Buffers 也就是上面公式中的 i.bufferram ,来自于 nr_blockdev_pages() 函数的返回值。
  • 交换区缓存 (swap cache)

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

# cat /proc/vmstat
...
nr_file_pages 587334
...

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

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

Swap Cache是什么?

用户进程的内存页分为两种: file-backed pages (与文件对应的内存页)和 anonymous pages (匿名页)。匿名页( anonymous pages )是没有关联任何文件的,比如用户进程通过 malloc() 申请的内存页,如果发生 swapping 换页,它们没有关联的文件进行回写,所以只能写入到交换区里。

交换区可以包括一个或多个交换区设备(裸盘、逻辑卷、文件都可以充当交换区设备),每一个交换区设备在内存里都有对应的 swap cache ,可以把 swap cache 理解为交换区设备的 page cache page cache 对应的是一个个文件, swap cache 对应的是一个个交换区设备,kernel管理 swap cache 与管理 page cache 一样,用的都是 radix-tree ,唯一的区别是: page cache 与文件的对应关系在打开文件时就确定了,而一个匿名页只有在即将被 swap-out 的时候才决定它会被放到哪一个交换区设备,即匿名页与 swap cache 的对应关系在即将被 swap-out 时才确立。

并不是每一个匿名页都在 swap cache 中,只有以下情形之一的匿名页才在:

  • 匿名页即将被 swap-out 时会先被放进 swap cache ,但通常只存在很短暂的时间,因为紧接着在 pageout 完成之后它就会从 swap cache 中删除,毕竟 swap-out 的目的就是为了腾出空闲内存;【注:参见 mm/vmscan.c: shrink_page_list() ,它调用的 add_to_swap() 会把 swap cache 页面标记成 dirty ,然后它调用 try_to_unmap() 将页面对应的 page table mapping 都删除,再调用 pageout() 回写 dirty page ,最后 try_to_free_swap() 会把该页从 swap cache中 删除。】
  • 曾经被 swap-out 现在又被 swap-in 的匿名页会在 swap cache 中,直到页面中的内容发生变化、或者原来用过的交换区空间被回收为止。【注:当匿名页的内容发生变化时会删除对应的 swap cache ,代码参见 mm/swapfile.c: reuse_swap_page() 。】

cached:

Cached 表示除去 buffers swap cache 之外,剩下的也就是普通文件的缓存页的数量:

global_page_state(NR_FILE_PAGES) – total_swapcache_pages – i.bufferram

所以关键还是要理解 buffers 是什么含义。

buffers:

从源代码中看到, buffers 来自于 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;
}

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

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

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

pagemap.h: add_to_page_cache
> filemap.c: add_to_page_cache_locked
C__add_to_page_cache_locked
> page_cache_tree_insert
和:
filemap.c: delete_from_page_cache
> __delete_from_page_cache
> page_cache_tree_delete
static inline int add_to_page_cache(struct page *page,
                struct address_space *mapping, pgoff_t offset, gfp_t gfp_mask)

{
        int error;
 
        __set_page_locked(page);
        error = add_to_page_cache_locked(page, mapping, offset, gfp_mask);
        if (unlikely(error))
                __clear_page_locked(page);
        return error;
}
 
void delete_from_page_cache(struct page *page)
{
        struct address_space *mapping = page->mapping;
        void (*freepage)(struct page *);
 
        BUG_ON(!PageLocked(page));
 
        freepage = mapping->a_ops->freepage;
        spin_lock_irq(&mapping->tree_lock);
        __delete_from_page_cache(page, NULL);
        spin_unlock_irq(&mapping->tree_lock);
        mem_cgroup_uncharge_cache_page(page);
 
        if (freepage)
                freepage(page);
        page_cache_release(page);
}

这两个函数是通用的, block device 和 文件 inode 都可以调用,至于更新的是块设备的( buffers )还是文件的( cached ),取决于参数变量 mapping 如果 mapping 对应的是块设备,那么相应的统计信息会反映在 buffers 中;如果 mapping 对应的是文件 inode ,影响的就是 cached 。我们下面看看 kernel 中哪些地方会把块设备的 mapping 传递进来。

首先是块设备本身,打开时使用 bdev->bd_inode->i_mapping

static int blkdev_open(struct inode * inode, struct file * filp)
{
        struct block_device *bdev;
 
        /*
         * Preserve backwards compatibility and allow large file access
         * even if userspace doesn't ask for it explicitly. Some mkfs
         * binary needs it. We might want to drop this workaround
         * during an unstable branch.
         */

        filp->f_flags |= O_LARGEFILE;
 
        if (filp->f_flags & O_NDELAY)
                filp->f_mode |= FMODE_NDELAY;
        if (filp->f_flags & O_EXCL)
                filp->f_mode |= FMODE_EXCL;
        if ((filp->f_flags & O_ACCMODE) == 3)
                filp->f_mode |= FMODE_WRITE_IOCTL;
 
        bdev = bd_acquire(inode);
        if (bdev == NULL)
                return -ENOMEM;
 
        filp->f_mapping = bdev->bd_inode->i_mapping;
 
        return blkdev_get(bdev, filp->f_mode, filp);
}

其次,文件系统的 Superblock 也是使用块设备:

struct super_block {
        ...
        struct block_device     *s_bdev;
        ...
}
 
int inode_init_always(struct super_block *sb, struct inode *inode)
{
...
        if (sb->s_bdev) {
                struct backing_dev_info *bdi;
 
                bdi = sb->s_bdev->bd_inode->i_mapping->backing_dev_info;
                mapping->backing_dev_info = bdi;
        }
...
}

sb表示 SuperBlock s_bdev 就是块设备。 Superblock 是文件系统的 metadata (元数据),不属于文件,没有对应的inode,所以,对 metadata 操作所涉及的缓存页都只能利用块设备 mapping ,算入 buffers 的统计值内。

如果文件含有间接块( indirect blocks ),因为间接块也属于 metadata ,所以走的也是块设备的 mapping 。查看源代码,果然如此:

ext4_get_blocks
->  ext4_ind_get_blocks
    ->  ext4_get_branch
        ->  sb_getblk
 
static inline struct buffer_head *
sb_getblk(struct super_block *sb, sector_t block)
{                       
        return __getblk(sb->s_bdev, block, sb->s_blocksize);
}

这样我们就知道了 buffers 是块设备( block device )占用的缓存页,分为两种情况:

  • 直接对块设备进行读写操作;
  • 文件系统的 metadata (元数据),比如 SuperBlock

验证:

现在我们来做个测试,验证一下上述结论。既然文件系统的 metadata 会用到 buffers ,我们用 find 命令扫描文件系统,观察 buffers 增加的情况:

# free
             total       used       free     shared    buffers     cached
Mem:       3848656    2889508     959148       5316     263896    2023340
-/+ buffers/cache:     602272    3246384
Swap:      2031612          0    2031612
 
# find / -name abc.def
 
# free
             total       used       free     shared    buffers     cached
Mem:       3848656    2984052     864604       5320     319612    2023348
-/+ buffers/cache:     641092    3207564
Swap:      2031612          0    2031612

再测试一下直接读取block device,观察 buffers 增加的现象:

# free
             total       used       free     shared    buffers     cached
Mem:       3848656    3006944     841712       5316     331020    2028648
-/+ buffers/cache:     647276    3201380
Swap:      2031612          0    2031612
 
# dd if=/dev/sda1 of=/dev/null count=2000
2000+0 records in
2000+0 records out
1024000 bytes (1.0 MB) copied, 0.026413 s, 38.8 MB/s
 
# free
             total       used       free     shared    buffers     cached
Mem:       3848656    3007704     840952       5316     331872    2028692
-/+ buffers/cache:     647140    3201516
Swap:      2031612          0    2031612

结论:

free 命令所显示的 buffers 表示块设备(block device)所占用的缓存页,包括直接读写块设备、以及文件系统元数据( metadata )如 SuperBlock 所使用的缓存页;而 cached 表示普通文件所占用的缓存页。



招已经开始啦,大家如果不做好充足准备的话, 招很难找到好工作。


送大家一份就业大礼包,大家可以突击一下春招,找个好工作!


 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: TI培训

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved