Linux VFS机制简析(二)

Linux VFS机制简析(二)

接上一篇Linux VFS机制简析(一),本篇继续介绍有关Address space和address operations、file和file operations、dentry和dentry operations和dentry cache API。

Address Space

Address Space用于管理page caches里的page页,它关联某个文件的所有pages,并管理文件的内容到进程地址空间的映射。它还提供了内存管理接口(page回收等)、根据地址查找page、跟踪page的tags(如dirty和writeback)等等功能。
VM模块会调用->write_page方法去尝试将脏页刷盘,以及调用->releasepage方法将clean page释放。带有PagePrivate标记的clean page(引用为0)会被VM直接释放而不通知Address Space。
为了实现这个功能,Address Space通过lru_cache_add()将page放入LRU,并通过mark_page_active()标记page正在使用。

Pages通过->index保存在一个radix树里,该radix树维护page的PG_Dirty和PG_Writeback信息,因此查找这两个标识的pages变得非常快。
Dirty标记(PAGECACHE_TAG_DIRTY)主要由->writepages(默认方法mpage_writepages())方法使用。它使用该标记查找脏页并调用->writepage方法。如果Address operations实现了自己的->writepages(不使用mpage_writepages),则Dirty标记将几乎没有作用。write_inode_now()和sync_inode()通过Dirty标记来检查->writepages是否成功完成。
Writeback标记主要是由filemap_wait 方法和sync_page* 方法使用,通过调用filemap_fdatawait_range()等待所有的writeback完成。如果定义了->sync_page,则会调用它来等待所有需要writeback的page结束。

Address Space Handler可以通过page的private字段保存额外的数据,此时需要设置PG_Private标识。这样VM的相关操作会调用address的handler处理这些数据。

上面说的这么多Page相关的管理,其实Address Space最核心的作用是充当存储和应用程序的中间缓存。数据从存储侧以page为单位读入address space,通过拷贝或者mapping的方式提供给应用层。应用写入数据到address space,然后通过writeback机制写入到存储。
读操作的核心是readpage()。写操作稍微复杂些,可以通过write_begin/write_end或者set_page_dirty写入数据到address space,再通过writepage、sync_page和writepages写入数据到存储。从address space里增加删除page由inode的i_mutex锁保护。

当数据写入到page,需要设置PG_Dirty标识,当writepage准备写入存储时清除PG_Dirty,并设置PG_Writeback标识,知道数据完全写入存储后清除PG_Writeback。

struct address_space_operations

struct address_space_operations的定义如下:

struct address_space_operations { int (*writepage)(struct page *page, struct writeback_control *wbc); int (*readpage)(struct file *, struct page *); int (*sync_page)(struct page *); int (*writepages)(struct address_space *, struct writeback_control *); int (*set_page_dirty)(struct page *page); int (*readpages)(struct file *filp, struct address_space *mapping, struct list_head *pages, unsigned nr_pages); int (*write_begin)(struct file *, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata); int (*write_end)(struct file *, struct address_space *mapping, loff_t pos, unsigned len, unsigned copied, struct page *page, void *fsdata); sector_t (*bmap)(struct address_space *, sector_t); int (*invalidatepage) (struct page *, unsigned long); int (*releasepage) (struct page *, int); void (*freepage)(struct page *); ssize_t (*direct_IO)(int, struct kiocb *, const struct iovec *iov, loff_t offset, unsigned long nr_segs); struct page* (*get_xip_page)(struct address_space *, sector_t, int); /* migrate the contents of a page to the specified target */ int (*migratepage) (struct page *, struct page *); int (*launder_page) (struct page *); int (*error_remove_page) (struct mapping *mapping, struct page *page); int (*swap_activate)(struct file *); int (*swap_deactivate)(struct file *); };

writepage:VM调用,用于将脏页写入后端存储。参数wbc->sync_mode显示是什么原因触发,\'sync\'或者\'flush\'(释放内存)。调用时PG_Dirty已经被清除,并且PageLocked已经设置。writepage开始写入数据时需要设置PG_Writeback,并且写入结束时清除该标记。无论是同步还是异步写入,都要保证函数返回时page处于unlocked状态。
如果wbc->sync_mode是WB_SYNC_NONE(不等待),则writepage遇到困难时可以不那么努力的写入,而是返回AOP_WRITEPAGE_ACTIVATE,这样VM不会老是来写该page。

readpage:VM调用,用于从后端存储读取数据。调用时,page处于lock状态,并且在读取结束时需要设置为unlock状态,并设置uptodate。如果readpage处理过程中需要unlock page,则unlcok之后需要返回AOP_TRUNCATED_PAGE,调用者将重新定位page并重新lock,成功之后会再次调用readpage。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwwysp.html