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回收内存.
阅读(2137) | 评论(0) | 转发(1) |