Linux内核源码分析-页高速缓存-address_space

来源:互联网 发布:新淘宝店铺转让价格表 编辑:程序博客网 时间:2024/05/18 01:52

本文主要参考《深入理解Linux内核》,结合2.6.11版的内核代码,分析内核文件子系统中的页高速缓存处理函数。
注意:
1、 不描述内核同步、错误处理、参数合法性验证相关的内容
2、 源码摘自Linux内核2.6.11 stable版,获取命令:
git clone
git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git
cd ./linux-stable/
git checkout linux-2.6.11.y
3、 阅读本文请结合《深入理解Linux内核》第三版相关章节
4、 本文会不定时更新

函数调用结构

这里写图片描述

公共函数

1、find_get_page
功能:
调用函数radix_tree_lookup在address_space 的radix_tree_root中查找页,找到则增加页的引用计数并返回,未找到返回NULL
源码:

/* * a rather lightweight function, finding and getting a reference to a * hashed page atomically. */struct page * find_get_page(struct address_space *mapping, unsigned long offset){    struct page *page;    spin_lock_irq(&mapping->tree_lock);    page = radix_tree_lookup(&mapping->page_tree, offset);    if (page)        page_cache_get(page);    spin_unlock_irq(&mapping->tree_lock);    return page;}

2、find_get_pages
功能:
调用函数radix_tree_gang_lookup从树中查找nr_pages个非空页,并增加找到页的引用计数
源码:

/** * find_get_pages - gang pagecache lookup * @mapping:    The address_space to search * @start:  The starting page index * @nr_pages:   The maximum number of pages * @pages:  Where the resulting pages are placed * * find_get_pages() will search for and return a group of up to * @nr_pages pages in the mapping.  The pages are placed at @pages. * find_get_pages() takes a reference against the returned pages. * * The search returns a group of mapping-contiguous pages with ascending * indexes.  There may be holes in the indices due to not-present pages. * * find_get_pages() returns the number of pages which were found. */unsigned find_get_pages(struct address_space *mapping, pgoff_t start,                unsigned int nr_pages, struct page **pages){    unsigned int i;    unsigned int ret;    spin_lock_irq(&mapping->tree_lock);    ret = radix_tree_gang_lookup(&mapping->page_tree,                (void **)pages, start, nr_pages);    for (i = 0; i < ret; i++)        page_cache_get(pages[i]);    spin_unlock_irq(&mapping->tree_lock);    return ret;}

3、find_lock_page
功能:
调用函数radix_tree_lookup在address_space 的radix_tree_root中查找页,找到则增加页的引用计数、锁定页并返回,未找到返回NULL
源码:

/** * find_lock_page - locate, pin and lock a pagecache page * * @mapping - the address_space to search * @offset - the page index * * Locates the desired pagecache page, locks it, increments its reference * count and returns its address. * * Returns zero if the page was not present. find_lock_page() may sleep. */struct page *find_lock_page(struct address_space *mapping,                unsigned long offset){    struct page *page;    spin_lock_irq(&mapping->tree_lock);repeat:    page = radix_tree_lookup(&mapping->page_tree, offset);    if (page) {        page_cache_get(page);        if (TestSetPageLocked(page)) {            spin_unlock_irq(&mapping->tree_lock);            lock_page(page);            spin_lock_irq(&mapping->tree_lock);            /* Has the page been truncated while we slept? */            if (page->mapping != mapping || page->index != offset) {                unlock_page(page);                page_cache_release(page);                goto repeat;            }        }    }    spin_unlock_irq(&mapping->tree_lock);    return page;}

4、find_trylock_page
功能:
和find_get_page功能类似,但试着锁定页,锁定失败返回NULL;不增加页的引用计数器
源码:

/* * Same as find_get_page, but trylock it instead of incrementing the count. */struct page *find_trylock_page(struct address_space *mapping, unsigned long offset){    struct page *page;    spin_lock_irq(&mapping->tree_lock);    page = radix_tree_lookup(&mapping->page_tree, offset);    if (page && TestSetPageLocked(page))        page = NULL;    spin_unlock_irq(&mapping->tree_lock);    return page;}

5、find_or_create_page
源码:

/** * find_or_create_page - locate or add a pagecache page * * @mapping - the page's address_space * @index - the page's index into the mapping * @gfp_mask - page allocation mode * * Locates a page in the pagecache.  If the page is not present, a new page * is allocated using @gfp_mask and is added to the pagecache and to the VM's * LRU list.  The returned page is locked and has its reference count * incremented. * * find_or_create_page() may sleep, even if @gfp_flags specifies an atomic * allocation! * * find_or_create_page() returns the desired page's address, or zero on * memory exhaustion. */struct page *find_or_create_page(struct address_space *mapping,        unsigned long index, unsigned int gfp_mask){    struct page *page, *cached_page = NULL;    int err;repeat:    page = find_lock_page(mapping, index);    if (!page) {        if (!cached_page) {            cached_page = alloc_page(gfp_mask);            if (!cached_page)                return NULL;        }        err = add_to_page_cache_lru(cached_page, mapping,                    index, gfp_mask);        if (!err) {            page = cached_page;            cached_page = NULL;        } else if (err == -EEXIST)            goto repeat;    }    if (cached_page)        page_cache_release(cached_page);    return page;}

处理流程:
1、 调用函数find_lock_page在mapping中查找页,找到则返回该页地址
2、 未找到调用函数alloc_page分配一页,调用函数add_to_page_cache_lru把页插入mapping中
6、add_to_page_cache
源码:

/* * This function is used to add newly allocated pagecache pages: * the page is new, so we can just run SetPageLocked() against it. * The other page state flags were set by rmqueue(). * * This function does not add the page to the LRU.  The caller must do that. */int add_to_page_cache(struct page *page, struct address_space *mapping,        pgoff_t offset, int gfp_mask){    int error = radix_tree_preload(gfp_mask & ~__GFP_HIGHMEM);    if (error == 0) {        spin_lock_irq(&mapping->tree_lock);        error = radix_tree_insert(&mapping->page_tree, offset, page);        if (!error) {            page_cache_get(page);            SetPageLocked(page);            page->mapping = mapping;            page->index = offset;            mapping->nrpages++;            pagecache_acct(1);        }        spin_unlock_irq(&mapping->tree_lock);        radix_tree_preload_end();    }    return error;}

处理流程:
1、 调用函数radix_tree_preload补充每cpu变量radix_tree_preloads中的radix_tree_node对象,并禁用内核抢占
2、 调用函数radix_tree_insert把页插入mapping->page_tree中
3、 插入成功则增加页引用计数器、锁定页,初始化page和mapping相关字段
4、 调用函数radix_tree_preload_end启动内核抢占
7、remove_from_page_cache
功能:
调用函数__remove_from_page_cache从mapping中删除页
源码:

void remove_from_page_cache(struct page *page){    struct address_space *mapping = page->mapping;    if (unlikely(!PageLocked(page)))        PAGE_BUG(page);    spin_lock_irq(&mapping->tree_lock);    __remove_from_page_cache(page);    spin_unlock_irq(&mapping->tree_lock);}

8、read_cache_page
源码:

/* * Read into the page cache. If a page already exists, * and PageUptodate() is not set, try to fill the page. */struct page *read_cache_page(struct address_space *mapping,                unsigned long index,                int (*filler)(void *,struct page*),                void *data){    struct page *page;    int err;retry:    page = __read_cache_page(mapping, index, filler, data);    if (IS_ERR(page))        goto out;    mark_page_accessed(page);    if (PageUptodate(page))        goto out;    lock_page(page);    if (!page->mapping) {        unlock_page(page);        page_cache_release(page);        goto retry;    }    if (PageUptodate(page)) {        unlock_page(page);        goto out;    }    err = filler(data, page);    if (err < 0) {        page_cache_release(page);        page = ERR_PTR(err);    } out:    return page;}

处理流程:
1、 调用函数__read_cache_page查找或分配新页
2、 调用函数mark_page_accessed记录也被访问标志
3、 调用函数PageUptodate检查页内容是否最新,是则返回页地址
4、 如果页内容不是最新,锁定页并调用函数mapping->a_ops->readpage读取页的内容,然后返回页地址

文件私有函数

1、add_to_page_cache_lru
功能:
调用函数add_to_page_cache把页插入mapping缓存中,插入成功调用函数lru_cache_add把页插入每cpu变量lru_add_pvecs中
源码:

int add_to_page_cache_lru(struct page *page, struct address_space *mapping,                pgoff_t offset, int gfp_mask){    int ret = add_to_page_cache(page, mapping, offset, gfp_mask);    if (ret == 0)        lru_cache_add(page);    return ret;}

2、__read_cache_page
源码:

static inline struct page *__read_cache_page(struct address_space *mapping,                unsigned long index,                int (*filler)(void *,struct page*),                void *data){    struct page *page, *cached_page = NULL;    int err;repeat:    page = find_get_page(mapping, index);    if (!page) {        if (!cached_page) {            cached_page = page_cache_alloc_cold(mapping);            if (!cached_page)                return ERR_PTR(-ENOMEM);        }        err = add_to_page_cache_lru(cached_page, mapping,                    index, GFP_KERNEL);        if (err == -EEXIST)            goto repeat;        if (err < 0) {            /* Presumably ENOMEM for radix tree node */            page_cache_release(cached_page);            return ERR_PTR(err);        }        page = cached_page;        cached_page = NULL;        err = filler(data, page);        if (err < 0) {            page_cache_release(page);            page = ERR_PTR(err);        }    }    if (cached_page)        page_cache_release(cached_page);    return page;}

处理流程:
1、 调用函数find_get_page在mapping中查找页,找到则返回该页的地址
2、 未找到则调用函数page_cache_alloc_cold分页一页,调用函数add_to_page_cache_lru把页插入mapping缓存中,调用函数mapping->a_ops->readpage读取页的内容,然后返回页地址

0 0
原创粉丝点击