(3) some func
static inline void set_page_dirty(struct page * page)color=green>
+
__set_page_dirty :标记页面为dirty,调整页面在page cache中(mapping)队列
的位置,并标记相关inode节点为dirty状态.调用者保证page在page cache之中.
void invalidate_inode_pages(struct inode * inode):color=green>
好像没有人用,正好也不看了.
(4)file truncate related
truncate_inode_pages ( service entry for file truncate in filemap.c)
+--->truncate_list_pages
+-->truncate_partial_page
+-->truncate_complete_page
这组函数和系统调用 truncate 相关(truncate file to specified len).入口
在 fs/open.c
asmlinkage long sys_truncate(const char * path, unsigned long length)
{
return do_sys_truncate(path, length);
}
经过一系列的函数周转到do_truncate->notify_change->inode_setattr(ext2文
件系统没有提供setattr,采用通用逻辑)->vmtruncate,最终利用truncate_inode
_pages清除page cache中相关的缓冲数据. 关于truncate不想再多说,只来看看:
static int truncate_list_pages(struct list_head *head, unsigned long
start, unsigned *partial)
/*注意一下加锁的顺序*/
{
.........
while (curr != head) {
unsigned long offset;
page = list_entry(curr, struct page, list);
curr = curr->next;
offset = page->index;
/* Is one of the pages to truncate? */
if ((offset >= start) || (*partial && (offset + 1) == start)) {
if (TryLockPage(page)) {
page_cache_get(page); /*先增加页面引用计数*/
spin_unlock(&pagecache_lock);/*然后才释放锁*/color=blue>
wait_on_page(page);
page_cache_release(page);
return 1;
}
/*先增加页面引用计数,然后才释放锁,注意这个顺序*/color=blue>
page_cache_get(page);
spin_unlock(&pagecache_lock);
.........
}
}
return 0;
}
(5)fsync, fdatasync
这两个系统调用将内核缓冲的文件数据同步到磁盘.系统调用的入口在buffer.c
sys_fsync,sys_fdatasync.区别在于sys_fsync将meta data也刷新到磁盘(atime
等),而sys_fdatasync只刷新"文件内容".两个系统调用都不保证包含他们的上级
目录的同步.如果需要,要明确的对对应目录调用fsync.
filemap.c中相关的函数是filemap_fdatasync,filemap_fdatawait.其作用是同
步page cache中的dirty页(mapping->dirty_pages)到磁盘.而inode meta data的
同步依赖于特定的文件系统(见buffer.c sys_fsync,注意page cache无meta数据).
filemap_fdatasync遍历dirty页面,提交系统驱动处理(mapping->a_ops->writepage
对ext2文件系统来讲就是ext2_aops -> ext2_writepage ->block_write_full_page
此函数也在buffer.c,请阅读此函数,注意page上的buffers并没有加入buffer cache)
filemap_fdatawait等待驱动完成page io操作.
不再列出相关代码,阅读时候体会一下加锁和增加页面引用计数的顺序.
(6)page cache: 数据读入
函数static inline int page_cache_read(struct file * file, unsigned
long offset)分配一个页面并提交磁盘驱动读入文件制定偏移的内容到page
cache, 同时考虑到了其他执行流先于我们读入的情况.仔细阅读此函数调用的
add_to_page_cache_unique->__add_to_page_cache,注意在__add_to_page_cache
中对page加了锁. 这个锁比较隐蔽,还以为page_cache_read在未加锁的情况下
启动了page io呢.
这是一个异步读取函数,应用于预读和其他需要异步读取的函数.
函数read_cluster_nonblocking调用page_cache_read异步读区整个cluster.
read_cache_page从mapping读取指定的内容到页面,所不同的是使用指定的方
式更新页面的内容.同样考虑到了各种race的情况.他使用用的函数有点拗口,来
看看:
static inline
struct page *__read_cache_page(struct address_space *mapping,
unsigned long index,
int (*filler)(void *,struct page*),
void *data)
{
struct page **hash = page_hash(mapping, index);
struct page *page, *cached_page = NULL;
int err;
repeat:
page = __find_get_page(mapping, index, hash);
if (!page) {/*未找到指定页面*/
if (!cached_page) {
cached_page = page_cache_alloc();
if (!cached_page)
return ERR_PTR(-ENOMEM);
}
page = cached_page;
/*
*add_to_page_cache_unique->__add_to_page_cache对页面进行了加锁
*/
if (add_to_page_cache_unique(page, mapping, index, hash))
goto repeat;color=blue>/*新页面加入cache的时候发现cache已经有了指定页面*/
cached_page = NULL;
err = filler(data, page); /*用指定方式更新页面*/
if (err < 0) {
page_cache_release(page);
page = ERR_PTR(err);
}
}
if (cached_page)
page_cache_free(cached_page);
return page;
}
从语义上讲函数read_cache_page应该是"读取到page cache".
还有一个逻辑上比较类似的函数grab_cache_page,此函数只是锁定一个指定
区间的页面,返回给调用者.而不管是否update,也不提交给驱动读取页面.
阅读(1827) | 评论(0) | 转发(0) |