好了,在了解了页高速缓存相关的数据结构以后,我们来介绍一下基本的页高速缓存处理函数:
对页高速缓存操作的基本高级函数有查找、增加和删除页。在以上函数的基础上还有另一个函数确保高速缓存包含指定页的最新版本。
1 查找页
函数find_get_page()接收的参数为指向address_space对象的指针和偏移量。它获取地址空间的自旋锁,并调用radix_tree_lookup()函数搜索拥有指定偏移量的基树的叶子节点:
struct page * find_get_page(struct address_space *mapping, unsigned long offset)
{
struct page *page;
read_lock_irq(&mapping->tree_lock);
page = radix_tree_lookup(&mapping->page_tree, offset);
if (page)
page_cache_get(page);
read_unlock_irq(&mapping->tree_lock);
return page;
}
函数radix_tree_lookup根据偏移量值中的位依次从树根开始并向下搜索,如上节所述。如果遇到空指针,函数返回NULL;否则,返回叶子节点的地址,也就是所需要的页描述符指针。如果找到了所需要的页,find_get_page()函数就增加该页的使用计数器,释放自旋锁,并返回该页的地址;否则,函数就释放自旋锁并返回NULL:
void *radix_tree_lookup(struct radix_tree_root *root, unsigned long index)
{
void **slot;
slot = __lookup_slot(root, index);
return slot != NULL ? *slot : NULL;
}
static inline void **__lookup_slot(struct radix_tree_root *root,
unsigned long index)
{
unsigned int height, shift;
struct radix_tree_node **slot;
height = root->height;
if (index > radix_tree_maxindex(height))
return NULL;
if (height == 0 && root->rnode)
return (void **)&root->rnode;
shift = (height-1) * RADIX_TREE_MAP_SHIFT;
slot = &root->rnode;
while (height > 0) {
if (*slot == NULL)
return NULL;
slot = (struct radix_tree_node **)
((*slot)->slots +
((index >> shift) & RADIX_TREE_MAP_MASK));
shift -= RADIX_TREE_MAP_SHIFT;
height--;
}
return (void **)slot;
}
函数find_get_pages()与find_get_page()类似,但它实现在高速缓存中查找一组具有相邻索引的页。它接收的参数是:指向address_space对象的指针、地址空间中相对于搜索起始位置的偏移量、所检索到页的最大数量、指向由该函数赋值的页描述符数组的指针。find_get_pages()依赖radix_tree_gang_lookup()函数实现查找操作,radix_tree_gang_lookup()函数为指针数组赋值并返回找到的页数。尽管由于一些页可能不在页高速缓存中而会出现空缺的页索引,但所返回的页还是递增的索引值。
还有另外几个函数实现页高速缓存上的查找操作。例如,find_lock_page()函数与find_get_page()类似,但它增加返回页的使用记数器,并调用lock_page()设置PG_locked标志,从而当函数返回时调用者能够以互斥的方式访问返回的页。随后,如果页已经被加锁,lock_Page()函数就阻塞当前进程。最后,它在PG_locked位置位时调用__wait_on_bit_lock()函数。后面的函数把当前进程置为TASK_UNINTERRUPTIBLE状态,把进程描述符存入等待队列,执行address_space对象的sync_page方法以取消文件所在块设备的请求队列,最后调用schedule()函数来挂起进程,直到把PG_locked标志清0。内核使用unlock_page()函数对页进行解锁,并唤醒在等待队列上睡眠的进程。
函数find_trylock_page()与find_lock_page()类似,仅有一点不