achlice
作者achlice·2018-02-02 14:55
系统工程师·h3c

linux下buffer与cache的区别

字数 3520阅读 2794评论 1赞 8

**本文作者:唐浩然
本文所有截图来自《深入理解linux内核-第三版》 DANIEL P.BOVET & MARCO CESATE著 陈莉君 张琼声 张宏伟 译 中国电力出版社**


先说总结:

1.Linux2.4.10之前的内核中,分两种disk cache, 分别为buffer cache和page cache,区别见文章最后两个截图(至于具体里面放什么结构的数据,我也不知道,还没看过2.4内核的块设备和VFS这部分内容)。

2.大约2.4.10后的内核,buffer cache已经不存在了(或者说换了一种数据存放的方法) 而这种页面的叫法也变了,叫做 buffer page, 而buffer page放在什么地主呢? 它放在page cache中。

听着很绕口, 或者可以这样讲: 2.4.10之后的内核的disk cache 只有 page cache, 而page cache中有些页面被叫做buffer page的,是因为这些页面(buffer page)都有与其相关的buffer_head 描述符,也正是这样页面被free 统计为 buffer 占用。如果没有buffer_head与该页相关,则被free统计为 cache占用。 Buffer page 与 buffer_head(下图中右边方框里的 缓冲区首部 )的关联如下 截图1:

缓冲区首部(buffer_head)的结构(include/linux/fs.h):

struct buffer_head {
/* First cache line: */
struct buffer_head *b_next; /* Hash queue list*/
unsigned long b_blocknr; /* block number */
unsigned short b_size; /* block size */
unsigned short b_list; /* List that this buffer appears */
kdev_t b_dev; /* device (B_FREE = free) */
atomic_t b_count; /* users using this block */
kdev_t b_rdev; /* Real device */
……
char * b_data; /* pointer to data block (512byte) */
struct page *b_page; /* the page this bh is mapped to */
void (*b_end_io)(struct buffer_head *bh, intuptodate); /* I/O completion */
void *b_private; /* reserved for b_end_io */
unsigned long b_rsector; /* Real buffer location on disk */
wait_queue_head_t b_wait;
struct inode * b_inode;
struct list_head b_inode_buffers; /* doubly
linked list of inode dirty buffers */
};

注意结构中下面的三个字段:

  • kdev_t b_dev:表示包含该块的块设备,通常是一个磁盘或分区
  • unsigned long b_blocknr;存放逻辑块号,表示块在磁盘或分区上的逻辑块号
  • struct page *b_page; 指向包含该块的页描述符

buffer_head 结构中以上三个字段把页中的内容和磁盘上的块直接关联起来。

下面来说说,buffer page里面放的是什么。

首先我们应该了解何时,内核会分配buffer page:

2.jpg

2.jpg

3.jpg
3.jpg

对于截图3中说的第一种情况,这里举个例子,如下面图中,如果该页属于同一个文件,且文件所在的文件系统的 block size 为1k, 页面中的4个block如果在磁盘上都不连续,那么这个页面就成为buffer page,且有与页中4个block相关的buffer_head结构:

4.jpg

4.jpg

5.jpg
5.jpg

对于截图3中所说的第二种情况:

6.jpg

6.jpg

由些可见,从磁盘读出来的文件在file system上的inode(ext3_inode ?)信息是放在buffer page里的,但是,这种page的所有者(如果一个cache页面是某文件的内容,那么我们说这个文件是此页面的所有都,或者属主)是哪个文件呢?往下看截图7的第二段:

7.jpg

7.jpg

Buffer Page的所有者(即属主)为被打开的这个设备文件, 且buffer page被插入到设备文件对应的 特殊文件系统(此处为bdev文件系统)的inode->address_space的page_tree中, 即:

  • bdev文件系统中为该设备建立的inode为 设备的 ”主inode“.
  • 设备文件自身(如/dev/sdb这个文件)对应的inode为设备的 ”次inode“

最后,直接访问一个设备文件会发生什么呢?架个虚拟机,启动一段时间后,待free输出cache和buffer部分稳定后,再执行下面内容:

#free 记录输出中buffer和cache的大小

#cat /proc/slabinfo |grep buffer_head 记录buffer_head的分配和使用数量

#cat /dev/sdb1 >/dev/null 这条命令执行完成后,再执行free 和cat /proc/slabinfo 查看输出 并和前面做对比。

buffer:

从 free 命令的代码里(项目地址 http://procps.sourceforge.net)
可以得知命令的数值实际上是 从 /proc/meminfo 来的,而 meminfo 的数值是从系统调用 sysinfo 来的。

具体详情 http://blog.csdn.net/lux_veritas/article/details/19231993

文章里提到了一个函数 si_meminfo mm/page_alloc.c

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() 得到的 fs/block_dev.c

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;
}

可以看到这个函数其实就是,遍历了所有的块设备,然后把这些设备 bd_inode 上的 pages 数目加起来。这些 page 由内核通过一颗 radix tree 来维护,而 nrpages 记录了tree pages 的数量。buffer 统计的是所有block device 对应的inode的 address_space的 page的数量。

如何增加 buffers ?

cat /dev/sda1 > /dev/null

所以可以确定的是直接访问 block 设备产生的 page cache是保存到 block device的 bd_inode 里面的。

另:关于linux 2.4.10之前内核对buffer和cache 的描述:

8.jpg

8.jpg

英文版:

9.jpg

9.jpg

如果觉得我的文章对您有用,请点赞。您的支持将鼓励我继续创作!

8

添加新评论1 条评论

wuwenpinwuwenpin软件开发工程师南京
2018-02-02 16:23
学习了
Ctrl+Enter 发表

作者其他文章

相关文章

相关问题

相关资料

X社区推广