address space的处理方法可以通过page::private附加一些额外的信息到page中。如果附加了信息,那么必须设置PG_Private。VM会调用address space的额外函数来处理这些数据。
address space作为存储和应用之间的中间层。每次从存储读取一页的数据到address space中,供应用读取。应用可以将任意大小的数据写入address space,但最后以页为单位写入到存储中。
读进程只需要readpage。写进程则复杂一点,需要write_begin/write_end将数据写入到address space,还需要writepage和writepages将数据写到存储中。
address space增删页受inode::i_mutex的保护。
当数据被写入到page中时,需要设置PG_Dirty(set_page_dirty())。当需要writepages时会设置PG_Writeback并清除PG_Dirty。标记为PG_Writeback的page可以在任意时间写回到存储,一旦写回完成后,该标志被清除。
写回会使用struct writeback_control给writepage和writepages提供写回的请求和限制信息,该结构也用于返回写回的结果。
处理写回的错误大部分使用缓存IO的应用都会周期性地调用同步接口(如fsync(),fdatasync(),msync(),sync_file_rage())来保证数据被写回到存储中。如果写回时发生错误,这些接口应当返回错误,但仅在第一次返回错误,后续应当返回0,除非又有新的数据写入且再次调用同步接口时又发生写回错误。
理想情况下,内核应当以文件为单位,返回其写回的错误。但实际上page cache并没有以文件为单位追踪脏页,因此当发生写回错误时,内核无法知道是哪个文件发生的写回错误。当前当发生写回错误时,内核给所有打开的文件都返回错误,即使这个文件并没有写入或者已经写回成功了。
想使用该框架的文件系统应当调用mapping_set_error()来记录错误。在写回数据后,文件系统还应当调用file_check_and_advance_wb_err()来确保struct file::f_wb_err记录的是正确的写回错误。
struct address_space_operationsVFS使用该结构操作文件的page cache。
struct address_space_operations { int (*writepage)(struct page *page, struct writeback_control *wbc); int (*readpage)(struct file *, 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); void (*invalidatepage) (struct page *, unsigned int, unsigned int); int (*releasepage) (struct page *, int); void (*freepage)(struct page *); ssize_t (*direct_IO)(struct kiocb *, struct iov_iter *iter); /* isolate a page for migration */ bool (*isolate_page) (struct page *, isolate_mode_t); /* migrate the contents of a page to the specified target */ int (*migratepage) (struct page *, struct page *); /* put migration-failed page back to right list */ void (*putback_page) (struct page *); int (*launder_page) (struct page *); int (*is_partially_uptodate) (struct page *, unsigned long, unsigned long); void (*is_dirty_writeback) (struct page *, bool *, bool *); int (*error_remove_page) (struct mapping *mapping, struct page *page); int (*swap_activate)(struct file *); int (*swap_deactivate)(struct file *); };writepage:基于数据完整性(sync)或者释放内存(flush)的原因,VM会调用该方法将脏页写回到存储。写回过程:清除PG_Dirty,设置PageLocked为true,然后writepage开始写回,并设置PG_Writeback,然后在完成写回后解锁page。
如果wbc->sync_mode是WB_SYNC_NONE,那么writepage在无法完成写回指定页的情况下,返回AOP_WRITEPAGE_ACTIVATE,这样VM就不会一直为该页调用writepage了。
readpage:VM调用该方法从存储中读取一页的数据。该页会被锁定,但需要在读取完成后,标记为uptodate并解锁。如果readpage需要解锁,也可以解锁,并返回AOP_TRUNCATED_PAGE。在这种情况下,VM会重新安置和加锁该页,再次调用readpage。
writepages:VM调用该方法将address space相关联的页都写回到存储。如果如果wbc->sync_mode是WBC_SYNC_ALL,那么writeback_control就会指定一组必须写回的页。如果是WBC_SYNC_NONE的话,则只要求尽可能写入nr_to_write页。如果该方法未定义,那么会用mpage_writepages来替代。该方法会从address space中获取所有标记为DIRTY的页,然后传递给writepage
set_page_dirty:VM调用该方法设置脏页。只有在address space中有私有数据的page且在页变脏时需要更新这些私有数据时,才需要该方法。如果定义了该方法,那么它必须设置PageDirty标志和基数树中的PAGECACHE_TAG_DIRTY标志。
readpages:VM调用该方法读取address space相关联的页。这个方法本质上是多次调用readpage。readpages只用于预读,因此读取错误被忽略了。