分类: LINUX
2015-07-11 13:57:24
原文地址:read&write页高速缓存查找分配关键比较 作者:zixin
Read读操作
// page 存放page cache中找到的页面
// cached_page存放不存在page cache时分配的页面
do_generic_file_read
。。。。。。。。。。。。。。。。。。。。。。。。。。
struct page *cached_page;
。。。。。。。。。。。。。。。。。。。。。。。。。。
cached_page = NULL;
。。。。。。。。。。。。。。。。。。。。。。。。。。
for (;;) {
struct page *page, **hash;
。。。。。。。。。。。。。。。。。。。。。。。。。。
在for循环中找高速缓存中的页面,找不到跳转
*************************************************************
//在page_hash_table找到其对应的杂凑队列
hash = page_hash(mapping, index);
//自旋锁
spin_lock(&pagecache_lock);
//搜索此队列,找到该页面page结构
page = __find_page_nolock(mapping, index, *hash);
问题1:比较写操作中,第一,他没有在这一步之前上自旋锁;第二他在队列中搜索的是用__find_lock_page,有何不同?
//没有在page cache中找到,跳到no_cached_page
if (!page)
goto no_cached_page;
*****************************************************************
在no_cached_page处调用page_cache_alloc分配新页面,且调用__add_to_page_cache将新页面加入高速缓存,调用lru_cache_add将其加入LRU
*************************************************************
no_cached_page:
。。。。。。。。。。。。。。。。。。。。。。。。。。
//页面没有缓存,分配新页面
if (!cached_page) {
//解自旋锁
spin_unlock(&pagecache_lock);
//分配建立一个缓冲页面(空白、空闲)
cached_page = page_cache_alloc(mapping);
。。。。。。。。。。。。。。。。。。。。。。。。。。
}
//将新页面加入hash表,有具体函数解析
page = cached_page;
__add_to_page_cache(page, mapping, index, hash);
//解自旋锁
spin_unlock(&pagecache_lock);
问题2:上一次已经解过自旋锁,而__add_to_page_cache并没有再次自旋锁,这里为什么还要解自旋锁呢?
//调用lru_cache_add把page增加到非活动链表(unactive_list)中
lru_cache_add(page);
cached_page = NULL;
//跳转到readpage
goto readpage;
}
*************************************************************
__add_to_page_cache解析
把新页面如何加入hash表,且这里关联设置了page标志
/*
* This adds a page to the page cache, starting out as locked,
* owned by us, but unreferenced, not uptodate and with no errors.
*/
static inline void __add_to_page_cache(struct page * page, struct address_space *mapping, unsigned long offset, struct page **hash)
{
unsigned long flags;
//清PG_uptodate、PG_error、PG_dirty、PG_referenced、PG_arch_1、PG_checked标志
flags = page->flags & ~(1 << PG_uptodate | 1 << PG_error | 1 << PG_dirty | 1 << PG_referenced | 1 << PG_arch_1 | 1 << PG_checked);
//设置页框PG_locked标志,加以上表示页已经被加锁并存在与高速缓存中但还没有填充数据
page->flags = flags | (1 << PG_locked);
//先增加页面引用计数
page_cache_get(page);
//初始化index字段,表示含有数据的页在页的磁盘映像中的位置
page->index = offset;
//调用add_page_to_inode_queque把page插入page cache对象的clean_pages中
add_page_to_inode_queue(mapping, page);
//调用add_page_to_hash_queue把page插入到散列表中
add_page_to_hash_queue(page, hash);
}
Write写操作
// page 存放page cache中找到的页面
// cached_page存放不存在page cache时分配的页面
generic_file_write
。。。。。。。。。。。。。。。。。。。。。。。。。。
struct page *cached_page;
。。。。。。。。。。。。。。。。。。。。。。。。。。
cached_page = NULL;
。。。。。。。。。。。。。。。。。。。。。。。。。。
do {
。。。。。。。。。。。。。。。。。。。。。。。。。。
page = __grab_cache_page(mapping, index, &cached_page);
。。。。。。。。。。。。。。。。。。。。。。。。。。
kaddr = kmap(page);
status = mapping->a_ops->prepare_write(file, page, offset, offset+bytes);
if (status)
goto sync_failure;
page_fault = __copy_from_user(kaddr+offset, buf, bytes);
flush_dcache_page(page);
conditional_schedule();
status = mapping->a_ops->commit_write(file, page, offset, offset+bytes);
。。。。。。。。。。。。。。。。。。。。。。。。。。
if (status >= 0) {
written += status;
count -= status;
pos += status;
buf += status;
。。。。。。。。。。。。。。。。。。。。。。。。。。
} while (count);
__grab_cache_page解析
在do循环调用的函数__grab_cache_page中把read的几个步骤综合起来,在页高速缓存中找页,没有找到分配新页以及将新页面加入hash
******************************************************
static inline struct page * __grab_cache_page(struct address_space *mapping,
unsigned long index, struct page **cached_page)
{
找高速缓存中的页面
*************************************************************
struct page *page, **hash = page_hash(mapping, index);
repeat:
page = __find_lock_page(mapping, index, hash);
*************************************************************
如果页没有缓存,调用page_cache_alloc分配新页面,且调用add_to_page_cache_unique 通过他来调用__add_to_page_cache将新页面加入高速缓存&调用lru_cache_add将其加入LRU
*************************************************************
if (!page) {
if (!*cached_page) {
*cached_page = page_cache_alloc(mapping);
if (!*cached_page)
return NULL;
}
page = *cached_page;
if (add_to_page_cache_unique(page, mapping, index, hash))
goto repeat;
*cached_page = NULL;
}
return page;
}
*************************************************************
add_to_page_cache_unique解析
调用__add_to_page_cache将新页面加入高速缓存&调用lru_cache_add将其加入LRU
*************************************************************
int add_to_page_cache_unique(struct page * page,
struct address_space *mapping, unsigned long offset,
struct page **hash)
{
int err;
struct page *alias;
//自旋锁
spin_lock(&pagecache_lock);
alias = __find_page_nolock(mapping, offset, *hash);
问题3: 这里为什么又要再一次__find_page_nolock呢?
我发现再每次__find_page_nolock前都需要自旋锁一次
err = 1;
if (!alias) {
__add_to_page_cache(page,mapping,offset,hash);(见上面已分析)
err = 0;
}
spin_unlock(&pagecache_lock);
if (!err)
lru_cache_add(page);
return err;
}
//add_page_to_inode_queue把page插入page cache对象的clean_pages中
static inline void add_page_to_inode_queue(struct address_space *mapping, struct page * page)
{
struct list_head *head = &mapping->clean_pages;
mapping->nrpages++;
list_add(&page->list, head);
page->mapping = mapping;
}
//add_page_to_hash_queue把page插入到散列表中
static void FASTCALL(add_page_to_hash_queue(struct page * page, struct page **p));
static void add_page_to_hash_queue(struct page * page, struct page **p)
{
struct page *next = *p;
*p = page;
page->next_hash = next;
page->pprev_hash = p;
if (next)
next->pprev_hash = &page->next_hash;
if (page->buffers)
PAGE_BUG(page);
atomic_inc(&page_cache_size);
}