free命令显示的buffers与cached的区别(2)

staticinline intadd_to_page_cache(structpage *page,
                structaddress_space *mapping,pgoff_t offset,gfp_t gfp_mask)
{
        interror;
 
        __set_page_locked(page);
        error=add_to_page_cache_locked(page,mapping,offset,gfp_mask);
        if(unlikely(error))
                __clear_page_locked(page);
        returnerror;
}
 
voidremove_from_page_cache(structpage *page)
{
        structaddress_space *mapping=page->mapping;
        void(*freepage)(structpage *)=NULL;
        structinode *inode=mapping->host;
 
        BUG_ON(!PageLocked(page));
 
        if(IS_AOP_EXT(inode))
                freepage=EXT_AOPS(mapping->a_ops)->freepage;
 
        spin_lock_irq(&mapping->tree_lock);
        __remove_from_page_cache(page);
        spin_unlock_irq(&mapping->tree_lock);
        mem_cgroup_uncharge_cache_page(page);
 
        if(freepage)
                freepage(page);
}

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

搜索内核源代码发现,ext4_readdir 函数调用 page_cache_sync_readahead 时传递的参数是 sb->s_bdev->bd_inode->i_mapping,其中s_bdev就是块设备,也就是说在读目录(ext4_readdir)的时候可能会增加 “buffers” 的值:

staticintext4_readdir(structfile *filp,
                        void*dirent,filldir_t filldir)
{
 
...
        structsuper_block *sb=inode->i_sb;
...
                        if(!ra_has_index(&filp->f_ra,index))
                                page_cache_sync_readahead(
                                        sb->s_bdev->bd_inode->i_mapping,
                                        &filp->f_ra,filp,
                                        index,1);
...
}

继续琢磨上面的代码,sb表示SuperBlock,属于文件系统的metadata(元数据),突然间一切恍然大悟:因为metadata不属于文件,没有对应的inode,所以,对metadata操作所涉及的缓存页都只能利用块设备mapping,算入 buffers 的统计值内。

打个岔:ext4_readdir() 中调用 page_cache_sync_readahead() 显然是在进行预读(read-ahead),为什么read-ahead没有使用普通文件inode的mapping,而是使用了底层的块设备呢?从记载在补丁中的说明来看,这是一个权宜之计,看这里,所以不必深究了。

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

ext4_get_blocks
->  ext4_ind_get_blocks
    ->  ext4_get_branch
        ->  sb_getblk
 
staticinline structbuffer_head *
sb_getblk(structsuper_block *sb,sector_t block)
{                     
        return__getblk(sb->s_bdev,block,sb->s_blocksize);
}

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

直接对块设备进行读写操作;
文件系统的metadata(元数据),比如 SuperBlock。
验证:
现在我们来做个测试,验证一下上述结论。既然读取EXT4文件系统的目录会使用到 “buffers”,我们用 find 命令扫描文件系统,观察 “buffers” 增加的情况:

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

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