Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1217259
  • 博文数量: 298
  • 博客积分: 10050
  • 博客等级: 上将
  • 技术积分: 3277
  • 用 户 组: 普通用户
  • 注册时间: 2007-03-14 13:36
文章分类
文章存档

2015年(7)

2012年(1)

2010年(5)

2009年(55)

2008年(73)

2007年(160)

我的朋友

分类: LINUX

2007-06-10 20:42:45

mm/filemap.c

  page cache,buffer cache,lru cache,swap cache



第一部分-->综述:



  首先概要介绍page cache和inode, page cache 和buffer cache,page cache

和swap cache,page cache和lru cache, buffer cache 和lru的相互关系.



0.page cache, buffer cache和lru cache的组成 

  filemap.c开头定义了一张hash表,是一个一维数组,每一项是一个指针,此指

针指向page结构.进入此hash表的page页面基本上就进入了page cache:



     struct page **page_hash_table;

page cache 还包括 struct address_space 内的几个队列(inode
queue):


    struct list_head clean_pages; /* list of clean pages */

    struct list_head dirty_pages; /* list of dirty pages */

    struct list_head locked_pages; /* list of locked pages */

   

  fs/buffer.c也有类似的hash数组,那是buffer cache.



  mm/page_alloc.c定义了两个lru队列: 


       struct list_head active_list;

       struct list_head inactive_dirty_list;

加上zone_t机构的

       struct list_head inactive_clean_list;

构成lru cache.

   

    page结构为这些cache 链表准备了几个成员变量:


typedef struct page {

struct list_head list; /*由buddy或者inode queue使用*/

struct address_space *mapping;

unsigned long index;

struct page *next_hash; /*page 在hash表中时,指向下一个page*/

atomic_t count;

unsigned long flags;

struct list_head lru; /*lru cache 使用*/

unsigned long age;

wait_queue_head_t wait;

struct page **pprev_hash;/*page在hash表中时,指向上一个节点

                          *指向自己的指针

                          */

struct buffer_head * buffers;

void *virtual; /* non-NULL if kmapped */

struct zone_struct *zone;

} mem_map_t;




    



1. page cache 和 inode

  page cache 在代码中又称 inode page cache, 足以显示page cache 和inode

紧密关联.加入page cache 和加入inode cache是同一个意思.加入page cache

意味着同时加入page cache hash表和inode queue(也建立了page和addr sapce

的关系). 见函数add_to_page_cache_locked,__add_to_page_cache即可取证.

从page cache 删除在程序中叫__remove_inode_page,再次显示inode 和page

cache的"一体化".

   加入/离开page cache还涉及到如下几个函数:


      add_page_to_hash_queue  /*加入pache cache hash表*/

      add_page_to_inode_queue /*加入inode queue即address_space*/

      remove_page_from_inode_queue

      remove_page_from_hash_queue

      __remove_inode_page   /*离开inode queue和hash 表*/

      remove_inode_page /*同上*/

      add_to_page_cache_locked /*加入inode queue,hash 和lru cache*/

      __add_to_page_cache  /*同上*/


   仅罗列函数add_page_to_hash_queue,以示完整:color=green>

static void add_page_to_hash_queue(struct page * page, struct page **p)

{

struct page *next = *p;



*p = page;              /*   page->newNode           */

page->next_hash = next; /*       +-----+             */

page->pprev_hash = p;   /*  p--> |hashp|-->|oldNode| */

if (next)               /*            next----+      */

next->pprev_hash = &page->next_hash;

if (page->buffers)

PAGE_BUG(page); /*证明page 不会同时存在于page cache

                和 buffer cache*/

                        /*2.6 已经与此不同了*/

atomic_inc(&page_cache_size);

}

  



2. page cache 和buffer cache

page 不会同时存在于 buffer cache 和 page cache.add_page_to_hash_queue

将此思想显露无余.buffer_head 定义在fs.h,和文件系统有着更为紧密的关系.

从文件读写角度看buffer cache缓存文件系统的管理信息像root entry, inod等,

而page cache缓存文件的内容.看看read 一个普通文件的流程:
color=green>

           sys_read ->file->f_op->read(以ext2为例) 

                             +

                  ext2_file_operations

                    +

           generic_file_read->do_generic_file_read(this file,filemap.c)

                                      +

           从page cache寻找指定页__find_page_nolock    

                                      +

           如果没有找到则从文件读取mapping->a_ops->readpage

                                             +

                                         ext2_aops

               +------<<<---------------<------+

       ext2_readpage->block_read_full_page(fs/buffer.c,buffer cache)
color=green>

    注意函数block_read_full_page,虽然位于buffer.c,但并没有使用buffer

cache. 但是确实使用了buffer:只是再指定page上创建buffer提交底层驱动读

取文件内容.这个流程有两个值得注意的地方,一是普通file的read通过page

cache进行,二是page cache读取的时候不和buffer cache进行同步,三是page

cache的确使用了buffer,不过注意,buffer 不是buffer cache.                              


          

    mmap也使用page cache 缓冲文件,流程如下:


          do_mmap->ext2_file_operations

                           +

                      generic_file_mmap

                           +

        以共享映射为例file_shared_mmap

                           +

               filemap_nopage(filemap,this file)先找page cache

                            +

                         ext2_aops 否则从文件读取

                            +

                     block_read_full_page

                           

如果打开象/dev/hda1这种设备文件,其内容缓存于buffer
cache,流程如下:


                      def_blk_fops

                           +

                     block_read(fs/block_dev.c)

                       +----->先用函数getblk从buffer cache查找

                       +----->否则使用ll_rw_block从驱动读取


注意到block_read和block_read_full_page都采用提交驱动的方式读取数据,

验证了page cache和buffer cache间的确没有数据同步.

   buffer cache 提供了getblk和bread两个接口,从buffer cache获取数据

搜索调用者的话,可以看到ext2文件系统从buffer cache获取的内容没有普通

文件的数据,而是inod,dentry等数据.







3.swap cache和page cache 

swap cache是一个特殊的page cache,不同之处在于address_space是swapper

_space.和page cache一样也挂入page cache hash queue.加入swap space的函

数add_to_swap_cache其实就是调用add_to_page_cache_locked.





4.page cache 和 lru cache

  进入page cache的页面必然加入lru cache(lru_cache_add).通过函数

__add_to_page_cache和add_to_page_cache_locked即可确信这一点.从page

cache 删除的时候也同时从lru cache删除. 搜索对__lru_cache_del的调用,

即可发现filemap,shmem,swap cache在使用到page cache的时候都是如此操作.

  注意,加入lru cache则不一定加入page cache,如 5)所述的buffer cache.

顺便述说一下lru cache相关的几个kthread和其大致作用:
color=green>

*****kswapd (mm/vmscan.c)

      +---->do_try_to_free_pages (如果内存已经不够用)

            +-->page_launder

            |    +-->扫描

            |    +-->对dirty页启动回写(包括mapping和buffer cache)

            +-->refill_inactive

                 +-->refill_inactive_scan

                    +-->扫描,选择合适页面移入

                       

                    +-->swap_out,对进程启动页面换出

                        +-->try_to_swap_out将选中页面放入

                           

      +----->refill_inactive_scan



*****kreclaimd(mm/vmscan.c)

      +----->遍历每个zone 用reclaim_page

             扫描zone->inactive_clean_pages,找出可以释放的页面

             脱离lru cache

      +----->对reclaim_page找到的页面补充到buddy系统

     

*****bdflush

      +---->flush_dirty_buffers (提交buffer cache到驱动)

      +----->如页面短缺,进行page_launder

         

     

5.buffer cache和lru队列

    buffer cache 的确也使用了lru队列,grow_buffers调用lru_cache_add将

页面加入lru队列.但是却没有加入到page cache.(请阅读代码)

    kreclaimd->reclaim_page将会尝试回收clean 页面到zone的buddy系统,如

果page->buffers不空,代表page被buffer cache 使用,那么reclaim_page只是将

页面转移到inactive_dirty_list.当reclaim_page发现buffer cache 的页面可

以回收时,因为此种页面不在page cache也不在swap cache, 只是从lru摘除,然

后直接释放.

    buffer cache如此使用lru cache,作为自己的垃圾回收方式.

   page_launder处理inactive_dirty_list将页面写入"硬盘",使页面可以释放

或者放入inactive_clean队列(大致描述).page_launder对buffer cache使用

的页面做特殊处理

       page_launder()    ------------>mm/vmscan.c

       if (page->buffers) {

          ...

            try_to_free_buffers

          ...

       }


try_to_free_buffers是buffer cache提供给lru的函数,buffer cache自己从不

使用,这证实了buffer cache的确利用lru cache回收内存.

阅读(2087) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~