date: 2014-10-03 19:09node
所謂內存頁面的週轉有兩方面的含義:其一是物理頁面的分配、使用和回收,並不必定涉及頁面的盤區交換;其二纔是盤區交換,盤區交換的最終目的也是爲了頁面回收。並不是全部的內存頁面均可以交換出去,只有映射到用戶空間的內存頁面纔會被換成,而內核即系統空間的頁面不在此列。須要指出,內核能夠訪問全部的內存頁面,換言之,全部物理頁面(HIGH_MEM區的高端物理內存除外)在系統空間中都有映射的,所謂「用戶空間的頁面」,是指在至少一個進程的用戶空間中有映射的頁面,反之就是內核使用的頁面。linux
顯然最簡單的頁面交換策略就是,每次缺頁異常時就分配一個內存頁面,並從磁盤頁面上讀入內容;若是沒有空閒的內存頁面可用分配,就設法將一個或多個內存頁面換出到磁盤此面上,從而騰出一些頁面來。但這種消極應對的策略有一個缺陷,這種臨時抱佛腳總髮生在系統忙碌的時候(系統忙着分配頁面哩)而沒有調度的餘地。比較積極的辦法是按期地,最好是在系統空閒時,挑選一些內存頁面換出而騰出依稀內存空間,保持必定的空閒內存供應量,使得在缺頁異常發生時,有頁面可分配。至於挑選的準則,通常都是LRU即「最近最少使用」準則。但這種策略實施起來比較困難,由於實際上並不存在一種準確預測頁面訪問的方法,可能上一秒剛剛把一個內存頁面換出,下一秒,系統又要訪問這個物理頁面,只好又把它趕快換進來,形成所謂的頁面「抖動」。app
爲了防止這種狀況發生,頁面的換出與釋放分兩步走。當系統挑選出若干頁面準備換出時,將這些頁面的內容寫入磁盤頁面,相應的頁面表項pte_t也變身爲swp_entry_t(P表中爲0,表示頁面再也不內存中),但所佔據的內存頁面並不當即釋放(內存頁面上的內容仍保留),而是將其page結構留在一個緩衝隊列中,使其從「活躍狀態」變爲「不活躍狀態」。就像NBA球員從「首發」變爲「替補」,至因而否要讓他去看「飲水機」即內存頁面的最終釋放,則要繼續考察一段時間再說。這樣若是一個頁面被換出後又被當即訪問從而觸發頁面異常,就能夠從緩衝隊列(替補席)裏找到對應的內存頁面,再次爲之創建映射(swp_entry_t變成pte_t),從「不活躍狀態」變爲「活躍狀態」。因爲此頁面還沒有被釋放,其上的內容還是有效的,就不須要從磁盤頁面上讀入內容了。反之,若是通過一段時間的老化,一個不活躍的頁面仍是沒有受到訪問,此時已經再也不有(用戶空間中的)虛存頁面映射到該內存頁面上了,該內存頁面到了最後釋放的時候了。函數
這種策略顯然能夠減小抖動,但仍是有改進的餘地。首先,在頁面換出時,若是自從上次頁面換出內存頁面中的內容並無改變,那麼這個頁面就是「乾淨」的,也就是與磁盤頁面上的內容一致。這樣的頁面固然不用費事再寫到磁盤頁面上。其次,即便是「髒」頁面,也沒必要當即寫出去,而能夠先從頁面映射表斷開,通過一段時間的「冷卻」或「老化」後再寫出去,從而變成「乾淨」的頁面。而至於「乾淨」的頁面,也不用着急當即釋放,能夠繼續緩衝直到真有必要時再釋放,由於回收一個乾淨的頁面,代價是很小的。spa
物理頁面週轉要點以下圖所示:code
圖中涉及到結構及變量說明以下:blog
<include/linux/fs.h> struct address_space { struct list_head clean_pages; /* list of clean pages */ struct list_head dirty_pages; /* list of dirty pages */ struct list_head locked_pages; /* list of locked pages */ unsigned long nrpages; /* number of total pages */ struct address_space_operations *a_ops; /* methods */ struct inode *host; /* owner: inode, block_device */ struct vm_area_struct *i_mmap; /* list of private mappings */ struct vm_area_struct *i_mmap_shared; /* list of shared mappings */ spinlock_t i_shared_lock; /* and spinlock protecting it */ }; <mm/swap_state.c> static struct address_space_operations swap_aops = { writepage: swap_writepage, sync_page: block_sync_page, }; //swapper_space的定義以下: struct address_space swapper_space = { LIST_HEAD_INIT(swapper_space.clean_pages), LIST_HEAD_INIT(swapper_space.dirty_pages), LIST_HEAD_INIT(swapper_space.locked_pages), 0, /* nrpages */ &swap_aops, };
swapper_space.a_ops被初始化成swap_aops,swap_aops定義的換出到磁盤頁面的函數爲swap_writepage。隊列
還記得page結構中有一個address_space結構成員mapping嗎?當把一個page加入到swapper_space中的某個隊列時,page. mapping就被賦值成swapper_space(的地址)。進程