Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1029000
  • 博文数量: 123
  • 博客积分: 5051
  • 博客等级: 大校
  • 技术积分: 1356
  • 用 户 组: 普通用户
  • 注册时间: 2008-07-14 10:56
文章分类
文章存档

2012年(1)

2011年(21)

2010年(13)

2009年(55)

2008年(33)

分类: LINUX

2010-04-07 21:30:17

页高速缓存引入
1、快速定位含有给定所有者相关数据的特定页;
2、记录在读或写页中的数据时应当如何处理高速缓存中的的每个页。

页高速缓存的核心数据结构是address_space对象。这是一个嵌入在页所有者的索引节点对象中的数据结构。
struct address_space {
    struct inode        *host;        /* owner: inode, block_device */
    struct radix_tree_root    page_tree;    /* radix tree of all pages */
    spinlock_t        tree_lock;    /* and lock protecting it */
    unsigned int        i_mmap_writable;/* count VM_SHARED mappings */
    struct prio_tree_root    i_mmap;        /* tree of private and shared mappings */
    struct list_head    i_mmap_nonlinear;/*list VM_NONLINEAR mappings */
    spinlock_t        i_mmap_lock;    /* protect tree, count, list */
    unsigned int        truncate_count;    /* Cover race condition with truncate */
    unsigned long        nrpages;    /* number of total pages */
    pgoff_t            writeback_index;/* writeback starts here */
    const struct address_space_operations *a_ops;    /* methods */
    unsigned long        flags;        /* error bits/gfp mask */
    struct backing_dev_info *backing_dev_info; /* device readahead, etc */
    spinlock_t        private_lock;    /* for use by the address_space */
    struct list_head    private_list;    /* ditto */
    struct address_space    *assoc_mapping;    /* ditto */
} __attribute__((aligned(sizeof(long))));
在linux中,通常会支持大到几个TB的文件,而对于这样的文件,当进行文件访问时,页高速缓存中可能充满了
很多的文件页,这样如果要进行顺序扫描,就会消耗掉大量的时间,linux在address_space中引入了一棵搜索树。
其中,page_tree字段是基树的根,下面是page_stree字段对应结构体的定义:
struct radix_tree_root {
    unsigned int         height;
    gfp_t            gfp_mask;
    struct radix_tree_node    *rnode;
};
struct radix_tree_node {
    unsigned int     height;
    unsigned int     count;
    struct rcu_head rcu_head;
    void         *slots[RADIX_TREE_MAP_SIZE];
    unsigned long    tags[RADIX_TREE_MAX_TAGS][RADIX_TREE_TAG_LONGS];
};
RADIX_TREE_MAP_SIZE    =    64;
RADIX_TREE_MAX_TAGS    =     2;
RADIX_TREE_TAG_LONGS    =     8;
page_stree字段指向所有者的页描述符号指针。这包含指向所有的页描述符的指针,给定的页索引表示页在所有者
磁盘影像中的位置。内核可以通过快速搜索来确定所需要的页是否在页高速缓存中。如果要查找所需要的页,内核将页
索引转换成基树中的路径,并快速找到页描述符所在的位置。如果找到了,内核可以从基树获得页描述符,而且可以
很快确定所找到的页是否是脏页,以及其数据的I/O传送的是否正在进行。

根据radix_tree_node的定义,会发现,基树中的每个节点可以有多到64个指针指向其他节点或页描述符。底层节点
存放指向页描述符的指针,上层的节点存放指向其他节点的指针。radix_tree_node中的slots字段指的即是这64个
指针的数组,count是记录节点中非空指针数量的计数器,tags是二维的标志数组。每一棵基树的根均由radix_tree_root
数据结构表示:height表示树的深度,gfp_mask表示新节点请求内存时所使用的标志,rnode指向与树中第一层节点
相应的数据结构radix_tree_node。基树节点中的tags数组作为基树的标记字段。
由于页索引仅仅放在32位变量中的缘故,当深度为6时,最高层的节点最多可以有4个孩子节点,实现如下:
如果深度为1,那么页索引只需要使用最低6位即可;如果深度为2,那么需要页索引的最低12位,其中高6位为第一层的
索引,低6位为第二层的索引...如此下去,就可以搜索完6层的基树,其中最高的那一层只能使用4个节点。

页高速缓存的处理函数
find_get_page    /* 已知address_space地址, 通过已给offset找出对应的page*/
/**
 * find_get_page - find and get a page reference
 * @mapping: the address_space to search
 * @offset: the page index
 *
 * Is there a pagecache struct page at the given (mapping, offset) tuple?
 * If yes, increment its refcount and return it; if no, return NULL.
 */
struct page *find_get_page(struct address_space *mapping, pgoff_t offset)
{
    void **pagep;
    struct page *page;

    rcu_read_lock();
repeat:
    page = NULL;
    pagep = radix_tree_lookup_slot(&mapping->page_tree, offset);
    /* 由基树的根,通过索引值offset得到相应的页 */
    if (pagep) {
        page = radix_tree_deref_slot(pagep);
        if (unlikely(!page || page == RADIX_TREE_RETRY))
            goto repeat;

        if (!page_cache_get_speculative(page))
            goto repeat;

        /*
         * Has the page moved?
         * This is part of the lockless pagecache protocol. See
         * include/linux/pagemap.h for details.
         */
        if (unlikely(page != *pagep)) {
            page_cache_release(page);
            goto repeat;
        }
    }
    rcu_read_unlock();

    return page;
}
radix_tree_lookup_slot的实现
/**
 *    radix_tree_lookup_slot    -    lookup a slot in a radix tree
 *    @root:        radix tree root
 *    @index:        index key
 *
 *    Returns:  the slot corresponding to the position @index in the
 *    radix tree @root. This is useful for update-if-exists operations.
 *
 *    This function can be called under rcu_read_lock iff the slot is not
 *    modified by radix_tree_replace_slot, otherwise it must be called
 *    exclusive from other writers. Any dereference of the slot must be done
 *    using radix_tree_deref_slot.
 */
void **radix_tree_lookup_slot(struct radix_tree_root *root, unsigned long index)
{
    unsigned int height, shift;
    struct radix_tree_node *node, **slot;
    ...
    node = radix_tree_indirect_to_ptr(node);
    /* 得到对应的树根节点 */
    height = node->height;
    /* 提取基树深度 */
    if (index > radix_tree_maxindex(height))
        return NULL;
    /* 超过最大深度则返回NULL */

    shift = (height-1) * RADIX_TREE_MAP_SHIFT;
    /* 计算左移值,由于基树的搜索是由高层向底层逐层解析搜索,所以shift的计算是关键 */

    do {
        slot = (struct radix_tree_node **)
            (node->slots + ((index>>shift) & RADIX_TREE_MAP_MASK));
        /* 搜索当前基树层,通过左移获得准确的slot值 */
        node = rcu_dereference(*slot);
        if (node == NULL)
            return NULL;

        shift -= RADIX_TREE_MAP_SHIFT;
        /* 左移值减RADIX_TREE_MAP_SHIFT(值的确定与系统结构有关) */
        height--;
        /* 搜索完一层,深度减少1 */
    } while (height > 0);

    return (void **)slot;
    /* 最终返回寻找到的slot */
}

向基树中增加页
static inline int add_to_page_cache(struct page *page, struct address_space *mapping,
                pgoff_t offset, gfp_t gfp_mask)
{
    int error;
    __set_page_locked(page);
    /* 为page设置PG_locked,防止其他内核路径并发访问该页 */
    error = add_to_page_cache_locked(page, mapping, offset, gfp_mask);
    /* 向页高速缓存增加一个页,成功返回0 */
    if (unlikely(error)) {
        __clear_page_locked(page);
    }
    return error;
}

从基树中删除页
remove_from_page_cache(struct page *page);

参考:《Understanding The Linux Kernel》
阅读(1869) | 评论(0) | 转发(0) |
0

上一篇:moblin安装

下一篇:C语言字符串处理大全

给主人留下些什么吧!~~