设为首页 加入收藏

TOP

Page Cache与Page回写(一)
2019-09-01 23:09:21 】 浏览:54
Tags:Page Cache 回写

综述

Page cache是通过将磁盘中的数据缓存到内存中,从而减少磁盘I/O操作,从而提高性能。此外,还要确保在page cache中的数据更改时能够被同步到磁盘上,后者被称为page回写(page writeback)。一个inode对应一个page cache对象,一个page cache对象包含多个物理page。

对磁盘的数据进行缓存从而提高性能主要是基于两个因素:第一,磁盘访问的速度比内存慢好几个数量级(毫秒和纳秒的差距)。第二是被访问过的数据,有很大概率会被再次访问。

Page Cache

Page cache由内存中的物理page组成,其内容对应磁盘上的block。page cache的大小是动态变化的,可以扩大,也可以在内存不足时缩小。cache缓存的存储设备被称为后备存储(backing store),注意我们在block I/O中提到的:一个page通常包含多个block,这些block不一定是连续的。

读Cache

当内核发起一个读请求时(例如进程发起read()请求),首先会检查请求的数据是否缓存到了page cache中,如果有,那么直接从内存中读取,不需要访问磁盘,这被称为cache命中(cache hit)。

如果cache中没有请求的数据,即cache未命中(cache miss),就必须从磁盘中读取数据。然后内核将读取的数据缓存到cache中,这样后续的读请求就可以命中cache了。page可以只缓存一个文件部分的内容,不需要把整个文件都缓存进来。

写Cache

当内核发起一个写请求时(例如进程发起write()请求),同样是直接往cache中写入,后备存储中的内容不会直接更新。内核会将被写入的page标记为dirty,并将其加入dirty list中。内核会周期性地将dirty list中的page写回到磁盘上,从而使磁盘上的数据和内存中缓存的数据一致。

Cache回收

Page cache的另一个重要工作是释放page,从而释放内存空间。cache回收的任务是选择合适的page释放,并且如果page是dirty的,需要将page写回到磁盘中再释放。理想的做法是释放距离下次访问时间最久的page,但是很明显,这是不现实的。下面先介绍LRU算法,然后介绍基于LRU改进的Two-List策略,后者是Linux使用的策略。

LRU算法

LRU(least rencently used)算法是选择最近一次访问时间最靠前的page,即干掉最近没被光顾过的page。原始LRU算法存在的问题是,有些文件只会被访问一次,但是按照LRU的算法,即使这些文件以后再也不会被访问了,但是如果它们是刚刚被访问的,就不会被选中。

Two-List策略

Two-List策略维护了两个list,active list 和 inactive list。在active list上的page被认为是hot的,不能释放。只有inactive list上的page可以被释放的。首次缓存的数据的page会被加入到inactive list中,已经在inactive list中的page如果再次被访问,就会移入active list中。两个链表都使用了伪LRU算法维护,新的page从尾部加入,移除时从头部移除,就像队列一样。如果active list中page的数量远大于inactive list,那么active list头部的页面会被移入inactive list中,从而位置两个表的平衡。

Page Cache在Linux中的具体实现

address_space结构

内核使用address_space结构来表示一个page cache,address_space这个名字起得很糟糕,叫page_ache_entity可能更合适。下面是address_space的定义

struct address_space {
    struct inode            *host;              /* owning inode */
    struct radix_tree_root  page_tree;          /* radix tree of all pages */
    spinlock_t              tree_lock;          /* page_tree lock */
    unsigned int            i_mmap_writable;    /* VM_SHARED ma count */
    struct prio_tree_root   i_mmap;             /* list of all mappings */
    struct list_head        i_mmap_nonlinear;   /* VM_NONLINEAR ma list */
    spinlock_t              i_mmap_lock;        /* i_mmap lock */
    atomic_t                truncate_count;     /* truncate re count */
    unsigned long           nrpages;            /* total number of pages */
    pgoff_t                 writeback_index;    /* writeback start offset */
    struct address_space_operations *a_ops;     /* operations table */
    unsigned                long flags;         /* gfp_mask and error flags */
    struct backing_dev_info *backing_dev_info;  /* read-ahead information */
    spinlock_t              private_lock;       /* private lock */
    struct list_head        private_list;       /* private list */
    struct address_space    *assoc_mapping;     /* associated buffers */
};

其中 host域指向对应的inode对象,host有可能为NULL,这意味着这个address_space不是和一个文件关联,而是和swap area相关,swap是Linux中将匿名内存(比如进程的堆、栈等,没有一个文件作为back store)置换到swap area(比如swap分区)从而释放物理内存的一种机制。page_tree保存了该page cache中所有的page,使用基数树(radix Tree)来存储。i_mmap是保存了所有映射到当前page cache(物理的)的虚拟内存区域(VMA)。nrpages是当前address_space中page的数量。

address_space操作函数

address_space中的a_ops域指向操作函数表(struct address_space_operations),每个后备存储都要实现这个函数表,比如ext3文件系统在fs/ext3/inode.c中实现了这个函数表。

内核使用函数表中的函数管理page cache,其中最重要的两个函数是readpage() 和writepage()

readpage()函数

readpage()首先会调用find_get_page(mapping, index)在page cache中寻找请求的数据,mapping是要寻找的page cache对象,即address_space对象,index是要读取的数据在文件中的偏移量。如果请求的数据不在该page cache中,那么内核就会创建一个新的p

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇高端内存映射之kmap持久内核映射-.. 下一篇痞子衡嵌入式:开启NXP-MCUBootUt..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目