struct address_space解讀

首先說的是Page Cache

address_space的操作將“文件”的某些部分映射到Linux page cache中的頁面中。此page cache表示已映射到內存的某些物理設備(例如磁盤)上的數據。物理設備通常對應於磁盤,但不一定必須如此。以這種方式,page cache包含來自最近訪問的“文件”的整個頁面。在頁面I / O操作(例如read()])中,內核檢查數據是否駐留在page cache中。如果數據在page cache中,則內核可以快速返回請求的頁面,而不必從磁盤讀取數據。

address_space

一個物理頁面可能包含多個不連續的物理塊,由於構成每個頁面的塊的不連續性,檢查page cache以查看是否已緩存某些數據變得更加困難。因此,不可能僅使用設備名稱和塊號來索引頁面緩存中的數據,否則這將是最簡單的解決方案。

例如,在x86架構上,物理頁面的大小爲4KB,而大多數文件系統上的磁盤塊可以小到512字節。因此,一個頁面中可能包含8個塊。塊不必是連續的,因爲文件本身可能會在整個磁盤上佈局。

此外,Linux page cache在它可以緩存的頁面方面相當普遍。實際上,System V Release 4中引入的原始page cache僅緩存文件系統數據。因此,SVR4 page cache使用等效的文件對象(稱爲struct vnode)來管理page cache。Linux page cache旨在緩存任何基於頁面的對象,其中包括許多形式的文件和內存映射。爲了保持通用性,Linux page cache使用address_space結構來標識page cache中的頁面。此結構在<linux / fs.h>中定義:

struct address_space {

struct inode *host; /* owner: inode, block_device */

struct radix_tree_root page_tree; /* radix tree of all pages */

spinlock_t tree_lock; /* and lock protecting it */

atomic_t i_mmap_writable;/* count VM_SHARED mappings */

struct rb_root_cached i_mmap; /* tree of private and shared mappings */

struct rw_semaphore i_mmap_rwsem; /* protect tree, count, list */

/* Protected by tree_lock together with the radix tree */

unsigned long nrpages; /* number of total pages */

/* number of shadow or DAX exceptional entries */

unsigned long nrexceptional;

pgoff_t writeback_index;/* writeback starts here */

const struct address_space_operations *a_ops; /* methods */

unsigned long flags; /* error bits */

spinlock_t private_lock; /* for use by the address_space */

gfp_t gfp_mask; /* implicit gfp mask for allocations */

struct list_head private_list; /* for use by the address_space */

void *private_data; /* ditto */

errseq_t wb_err;

} __attribute__((aligned(sizeof(long)))) __randomize_layout;

i_mmap字段是此地址空間中所有共享和私有映射的優先級搜索樹,優先級搜索樹是堆和基數樹的巧妙組合;地址空間中總共有nrpages;address_space一般與inode內核對象相關聯,如果是這樣,則host字段指向關聯的inode,而如果相關聯的對象不是inode,則host會是NULL,比如address_space與swapper相關聯的情況;a_ops字段與操作的接口函數由struct address_space_operations表示,並且也在<linux / fs.h>中定義,還是直接給個圖來說明:

readpage()和writepage()方法是最重要的,頁面讀取操作中涉及的步驟有:首先,向readpage()方法傳遞一個address_space加偏移量對,這些值用於在頁面緩存中搜索所需的數據:page = find_get_page(mapping, index);此處,mapping是給定的地址空間,而index是文件中所需的位置,如果頁面不在緩存中,則分配一個新頁面並將其添加到page cache中(從pagecache_get_page截取的部分關鍵代碼):

page = find_get_entry(mapping, offset);

if (!page){

__page_cache_alloc(gfp_mask);

add_to_page_cache_lru(page, mapping, offset, gfp_mask & GFP_RECLAIM_MASK);

}

最後,可以從磁盤讀取請求的數據,將其添加到page cache中,然後返回給用戶:

error = mapping->a_ops->readpage(file, page);

寫操作有些不同。對於文件映射,只要修改頁面,VM就會簡單地調用SetPageDirty(page);

內核隨後通過writepage()方法將頁面寫出。對特定文件的寫操作更加複雜。基本上,mm / filemap.c中的通用寫入路徑執行以下步驟:

page = __grab_cache_page(mapping, index, &cached_page, &lru_pvec);

status = a_ops->prepare_write(file, page, offset, offset+bytes);

page_fault = filemap_copy_from_user(page, offset, buf, bytes);

status = a_ops->commit_write(file, page, offset, offset+bytes);

首先,在page cache中搜索所需的頁面。如果它不在高速緩存中,則會分配並添加一個條目。接下來,調用prepare_write()方法來設置寫請求。然後將數據從用戶空間複製到內核緩衝區。最後,數據通過commit_write()函數寫入磁盤。由於前面的步驟是在所有頁面I / O操作期間執行的,因此可以確保所有頁面I / O都經過page cache。因此,內核嘗試滿足來自page cache的所有讀取請求。如果失敗,將從磁盤讀取頁面並將其添加到page cache中。對於寫操作,page cache充當寫操作的臨時基礎。因此,所有寫入的頁面也將添加到page cache中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章