內核采用一種通用的地址空間方案,來建立緩存數據與其來源之間的關聯。
1)? 內存中的頁分配到每個地址空間。這些頁的內容可以由用戶進程或內核本身使用各式各樣的方法操作。這些數據表示了緩存中的內容;
2)? 后備存儲器struct backing_dev_info指定了填充地址空間中頁的數據的來源。地址空間關聯到處理器的虛擬地址空間,是由處理器在虛擬內存中管理的一個區域到設備device上對應位置之間的一個映射。
如果訪問了虛擬內存中的某個位置,該位置沒有關聯到物理內存頁,內核可根據地址空間結構來找到讀取數據的來源。
為支持數據傳輸,每個地址空間都提供了一組操作,以容許地址空間所涉及雙方面的交互。
地址空間是內核中最關鍵的數據結構之一,對該數據結構的管理,已經演變為內核面對的最關鍵的問題之一。 頁緩存的任務在于,獲得一些物理內存頁,以加速在塊設備上按頁為單位執行的操作。
內核使用了基數樹來管理與一個地址空間相關的所有頁,以便盡可能降低開銷。對于基數樹的理解在這里就不分析了,后面有空的時候再做分析。
地址空間操作
[cpp]
structaddress_space_operations?{
/*將地址空間的一頁或多頁寫回到底層設備
這是通過向塊層發出一個相應的請求來完成的*/
int(*writepage)(structpage?*page,structwriteback_control?*wbc);
/*從后備存儲器將一頁或多個連續的頁讀入頁幀*/
int(*readpage)(structfile?*,structpage?*);
/*對尚未回寫到后備存儲器的數據進行同步*/
void(*sync_page)(structpage?*);
/*?Write?back?some?dirty?pages?from?this?mapping.?*/
int(*writepages)(structaddress_space?*,structwriteback_control?*);
/*?Set?a?page?dirty.??Return?true?if?this?dirtied?it?*/
int(*set_page_dirty)(structpage?*page);
int(*readpages)(structfile?*filp,structaddress_space?*mapping,
structlist_head?*pages,?unsigned?nr_pages);
/*執行由write系統調用觸發的寫操作*/
int(*write_begin)(structfile?*,structaddress_space?*mapping,
loff_t?pos,?unsigned?len,?unsigned?flags,
structpage?**pagep,void**fsdata);
int(*write_end)(structfile?*,structaddress_space?*mapping,
loff_t?pos,?unsigned?len,?unsigned?copied,
structpage?*page,void*fsdata);
/*?Unfortunately?this?kludge?is?needed?for?FIBMAP.?Don't?use?it?*/
sector_t?(*bmap)(structaddress_space?*,?sector_t);
void(*invalidatepage)?(structpage?*,?unsignedlong);
int(*releasepage)?(structpage?*,?gfp_t);
ssize_t?(*direct_IO)(int,structkiocb?*,conststructiovec?*iov,
loff_t?offset,?unsignedlongnr_segs);
int(*get_xip_mem)(structaddress_space?*,?pgoff_t,int,
void**,?unsignedlong*);
/*?migrate?the?contents?of?a?page?to?the?specified?target?*/
int(*migratepage)?(structaddress_space?*,
structpage?*,structpage?*);
int(*launder_page)?(structpage?*);
int(*is_partially_uptodate)?(structpage?*,?read_descriptor_t?*,
unsignedlong);
int(*error_remove_page)(structaddress_space?*,structpage?*);
};
頁面緩存的實現基于基數樹,緩存屬于內核中性能要求最苛刻的部分之一,而且廣泛用于內核的所有子系統,實現也比較簡單。舉兩個例子,其他的暫時不做分析了。
分配頁面用于加入地址空間
[cpp]
/*從伙伴系統中分配頁面,頁面的標志根據地址空間中的標志進行設置*/
staticinlinestructpage?*page_cache_alloc(structaddress_space?*x)
{
return__page_cache_alloc(mapping_gfp_mask(x));
}
分配完了添加到基數樹中
[cpp]
/*
*?Like?add_to_page_cache_locked,?but?used?to?add?newly?allocated?pages:
*?the?page?is?new,?so?we?can?just?run?__set_page_locked()?against?it.
*/
staticinlineintadd_to_page_cache(structpage?*page,
structaddress_space?*mapping,?pgoff_t?offset,?gfp_t?gfp_mask)
{
interror;
__set_page_locked(page);
/*實際的添加工作*/
error?=?add_to_page_cache_locked(page,?mapping,?offset,?gfp_mask);
if(unlikely(error))
__clear_page_locked(page);
returnerror;
}
[cpp]
/**
*?add_to_page_cache_locked?-?add?a?locked?page?to?the?pagecache
*?@page:???page?to?add
*?@mapping:????the?page's?address_space
*?@offset:?page?index
*?@gfp_mask:???page?allocation?mode
*
*?This?function?is?used?to?add?a?page?to?the?pagecache.?It?must?be?locked.
*?This?function?does?not?add?the?page?to?the?LRU.??The?caller?must?do?that.
*/
intadd_to_page_cache_locked(structpage?*page,structaddress_space?*mapping,
pgoff_t?offset,?gfp_t?gfp_mask)
{
interror;
VM_BUG_ON(!PageLocked(page));
error?=?mem_cgroup_cache_charge(page,?current->mm,
gfp_mask?&?GFP_RECLAIM_MASK);
if(error)
gotoout;
/*樹的相關結構申請*/
error?=?radix_tree_preload(gfp_mask?&?~__GFP_HIGHMEM);
if(error?==?0)?{
page_cache_get(page);/*使用計數加一*/
page->mapping?=?mapping;
page->index?=?offset;
spin_lock_irq(&mapping->tree_lock);
/*實際的插入操作*/
error?=?radix_tree_insert(&mapping->page_tree,?offset,?page);
if(likely(!error))?{
mapping->nrpages++;
__inc_zone_page_state(page,?NR_FILE_PAGES);
if(PageSwapBacked(page))
__inc_zone_page_state(page,?NR_SHMEM);
spin_unlock_irq(&mapping->tree_lock);
}else{
page->mapping?=?NULL;
spin_unlock_irq(&mapping->tree_lock);
mem_cgroup_uncharge_cache_page(page);
page_cache_release(page);
}
radix_tree_preload_end();
}else
mem_cgroup_uncharge_cache_page(page);
out:
returnerror;
}