分析文件页缓冲结构

inode结构定义了操作数据文件的函数表i_fop,它是文件系统提供的面向用户的高级文件IO接口.inode结构还定义了i_mapping指针,用它来描述对文件的IO缓冲.i_mapping->a_ops是文件系统提供的一组低级文件IO函数表,a_ops与块设备接口.在通常情况下,i_fop并不直接与块设备接口,而是间接通过a_ops读写文件.文件的页缓冲(page cache)就是i_fop与a_ops之间的缓冲,它是块设备缓冲之上的更高一级缓冲,直接用于具体文件的读写.

页缓冲用page_hash_table来索引不同文件不同位置上的页块.page_hash_table是页面结构指针(struct page*)组成的数组,它的键由i_mapping地址和文件的页块号组成,i_mapping指向inode结构中的i_data?br>峁?它的值对不同的inode是不同的,它的尺寸分配和dentry_hashtable的分配类似,每4M内存分配1个4K页面,每个页面则1024项.

page_hash(mapping,index)
以mapping:index为键返回page_hash_table中相应槽位的地址;

add_to_page_cache(page,mapping,index)
将page链入以mapping:index为键值的page_hash_table中;

add_to_page_cache_unique(page,mapping,index,hash)
在槽位hash上唯一地添加以mapping:index表示的页page;

__find_page_nolock(mapping,index,page)
在page_hash_table中查询以mapping:index为键的page;

remove_page_from_hash_queue(page)
将page从page_hash_table中删除;


typedef struct page {
struct list_head list;
struct address_space *mapping; 为inode->i_mapping的值
unsigned long index; 文件的页块号
struct page *next_hash; 用以形成hash链
atomic_t count; 引用计数
unsigned long flags;
struct list_head lru;
unsigned long age;
wait_queue_head_t wait;
struct page **pprev_hash; 指向hash链中前一页next_hash的地址
struct buffer_head * buffers;
void *virtual; 
struct zone_struct *zone;
} mem_map_t;
struct address_space {
struct list_head clean_pages; 加入page_hash_table后,与page->list相环接
struct list_head dirty_pages;
struct list_head locked_pages;
unsigned long nrpages; 表示该inode具有的文件缓冲页数量
struct address_space_operations *a_ops;
struct inode *host;
struct vm_area_struct *i_mmap;
struct vm_area_struct *i_mmap_shared; 
spinlock_t i_shared_lock;  
};
struct address_space_operations {
int (*writepage)(struct page *);
int (*readpage)(struct file *, struct page *);
int (*sync_page)(struct page *);
int (*prepare_write)(struct file *, struct page *, unsigned, unsigned);
int (*commit_write)(struct file *, struct page *, unsigned, unsigned);
int (*bmap)(struct address_space *, long);
};

#define page_hash(mapping,index) (page_hash_table+_page_hashfn(mapping,index))

atomic_t page_cache_size = ATOMIC_INIT(0);
unsigned int page_hash_bits;
struct page **page_hash_table;
extern inline unsigned long _page_hashfn(struct address_space * mapping, unsigned long
index)
{
#define i (((unsigned long) mapping)/(sizeof(struct inode) & ~ (sizeof(struct inode) -
1)))
#define s(x) ((x)+((x)>>PAGE_HASH_BITS))
return s(i+index) & (PAGE_HASH_SIZE-1);
#undef i
#undef s
}

void add_to_page_cache(struct page * page, struct address_space * mapping, unsigned
long offset)
{
spin_lock(&pagecache_lock);
__add_to_page_cache(page, mapping, offset, page_hash(mapping, offset));
spin_unlock(&pagecache_lock);
}
static inline void __add_to_page_cache(struct page * page,
struct address_space *mapping, unsigned long offset,
struct page **hash)
{
unsigned long flags;

if (PageLocked(page))
BUG();

flags = page->flags & ~((1 << PG_uptodate) | (1 << PG_error) | (1 << PG_dirty) | (1
<< PG_referenced) | (1 << PG_arch_1));
page->flags = flags | (1 << PG_locked);
page_cache_get(page); 增加引用计数
page->index = offset;
add_page_to_inode_queue(mapping, page);
将page->list与inode->i_mapping->clean_pages相环接
add_page_to_hash_queue(page, hash); 将page加入hash槽位
lru_cache_add(page); 与active_list相环接
}
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; 让page->mapping指向inode->i_mapping
}
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);
}
static inline struct page * __find_page_nolock(struct address_space *mapping, unsigned
long offset, struct page *page)
{
goto inside;

for (;;) {
page = page->next_hash;
inside:
if (!page)
goto not_found;
if (page->mapping != mapping)
continue;
if (page->index == offset)
break;
}
/*
* Touching the page may move it to the active list.
* If we end up with too few inactive pages, we wake
* up kswapd.
*/
age_page_up(page);
if (inactive_shortage() > inactive_target / 2 && free_shortage())
wakeup_kswapd();
not_found:
return page;
}
void add_to_page_cache_locked(struct page * page, struct address_space *mapping,
unsigned long index)
{
if (!PageLocked(page))
BUG();

page_cache_get(page);
spin_lock(&pagecache_lock);
page->index = index;
add_page_to_inode_queue(mapping, page);
add_page_to_hash_queue(page, page_hash(mapping, index));
lru_cache_add(page);
spin_unlock(&pagecache_lock);
}
void add_to_page_cache(struct page * page, struct address_space * mapping, unsigned
long offset)
{
spin_lock(&pagecache_lock);
__add_to_page_cache(page, mapping, offset, page_hash(mapping, offset));
spin_unlock(&pagecache_lock);
}


static 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);

err = 1;
if (!alias) {
__add_to_page_cache(page,mapping,offset,hash);
err = 0;
}

spin_unlock(&pagecache_lock);
return err;
}

static inline void remove_page_from_hash_queue(struct page * page)
{
struct page *next = page->next_hash;
struct page **pprev = page->pprev_hash;

if (next)
next->pprev_hash = pprev;
*pprev = next;
page->pprev_hash = NULL;
atomic_dec(&page_cache_size);
}

static inline void remove_page_from_inode_queue(struct page * page)
{
struct address_space * mapping = page->mapping;

mapping->nrpages--;
list_del(&page->list);
page->mapping = NULL;
}

void remove_inode_page(struct page *page)
{
if (!PageLocked(page))
PAGE_BUG(page);

spin_lock(&pagecache_lock);
__remove_inode_page(page);
spin_unlock(&pagecache_lock);
}
/*
* Remove a page from the page cache and free it. Caller has to make
* sure the page is locked and that nobody else uses it - or that usage
* is safe.
*/
void __remove_inode_page(struct page *page)
{
if (PageDirty(page)) BUG();
remove_page_from_inode_queue(page);
remove_page_from_hash_queue(page);
page->mapping = NULL;
}


void __init page_cache_init(unsigned long mempages)
{
unsigned long htable_size, order;

htable_size = mempages;
htable_size *= sizeof(struct page *);
for(order = 0; (PAGE_SIZE << order) < htable_size; order++)
;

do {
unsigned long tmp = (PAGE_SIZE << order) / sizeof(struct page *);

page_hash_bits = 0;
while((tmp >>= 1UL) != 0UL)
page_hash_bits++;

page_hash_table = (struct page **)
__get_free_pages(GFP_ATOMIC, order);
} while(page_hash_table == NULL && --order > 0);

printk("Page-cache hash table entries: %d (order: %ld, %ld bytes)/n",
       (1 << page_hash_bits), order, (PAGE_SIZE << order));
if (!page_hash_table)
panic("Failed to allocate page hash table/n");
memset((void *)page_hash_table, 0, PAGE_HASH_SIZE * sizeof(struct page *));
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值