內存管理之10:內存頁面的週轉

date: 2014-10-03 19:09node

1 頁面交換策略

所謂內存頁面的週轉有兩方面的含義:其一是物理頁面的分配、使用和回收,並不必定涉及頁面的盤區交換;其二纔是盤區交換,盤區交換的最終目的也是爲了頁面回收。並不是全部的內存頁面均可以交換出去,只有映射到用戶空間的內存頁面纔會被換成,而內核即系統空間的頁面不在此列。須要指出,內核能夠訪問全部的內存頁面,換言之,全部物理頁面(HIGH_MEM區的高端物理內存除外)在系統空間中都有映射的,所謂「用戶空間的頁面」,是指在至少一個進程的用戶空間中有映射的頁面,反之就是內核使用的頁面。linux

顯然最簡單的頁面交換策略就是,每次缺頁異常時就分配一個內存頁面,並從磁盤頁面上讀入內容;若是沒有空閒的內存頁面可用分配,就設法將一個或多個內存頁面換出到磁盤此面上,從而騰出一些頁面來。但這種消極應對的策略有一個缺陷,這種臨時抱佛腳總髮生在系統忙碌的時候(系統忙着分配頁面哩)而沒有調度的餘地。比較積極的辦法是按期地,最好是在系統空閒時,挑選一些內存頁面換出而騰出依稀內存空間,保持必定的空閒內存供應量,使得在缺頁異常發生時,有頁面可分配。至於挑選的準則,通常都是LRU即「最近最少使用」準則。但這種策略實施起來比較困難,由於實際上並不存在一種準確預測頁面訪問的方法,可能上一秒剛剛把一個內存頁面換出,下一秒,系統又要訪問這個物理頁面,只好又把它趕快換進來,形成所謂的頁面「抖動」。app

爲了防止這種狀況發生,頁面的換出與釋放分兩步走。當系統挑選出若干頁面準備換出時,將這些頁面的內容寫入磁盤頁面,相應的頁面表項pte_t也變身爲swp_entry_t(P表中爲0,表示頁面再也不內存中),但所佔據的內存頁面並不當即釋放(內存頁面上的內容仍保留),而是將其page結構留在一個緩衝隊列中,使其從「活躍狀態」變爲「不活躍狀態」。就像NBA球員從「首發」變爲「替補」,至因而否要讓他去看「飲水機」即內存頁面的最終釋放,則要繼續考察一段時間再說。這樣若是一個頁面被換出後又被當即訪問從而觸發頁面異常,就能夠從緩衝隊列(替補席)裏找到對應的內存頁面,再次爲之創建映射(swp_entry_t變成pte_t),從「不活躍狀態」變爲「活躍狀態」。因爲此頁面還沒有被釋放,其上的內容還是有效的,就不須要從磁盤頁面上讀入內容了。反之,若是通過一段時間的老化,一個不活躍的頁面仍是沒有受到訪問,此時已經再也不有(用戶空間中的)虛存頁面映射到該內存頁面上了,該內存頁面到了最後釋放的時候了。函數

這種策略顯然能夠減小抖動,但仍是有改進的餘地。首先,在頁面換出時,若是自從上次頁面換出內存頁面中的內容並無改變,那麼這個頁面就是「乾淨」的,也就是與磁盤頁面上的內容一致。這樣的頁面固然不用費事再寫到磁盤頁面上。其次,即便是「髒」頁面,也沒必要當即寫出去,而能夠先從頁面映射表斷開,通過一段時間的「冷卻」或「老化」後再寫出去,從而變成「乾淨」的頁面。而至於「乾淨」的頁面,也不用着急當即釋放,能夠繼續緩衝直到真有必要時再釋放,由於回收一個乾淨的頁面,代價是很小的。spa

2 一個內存頁面的旅行

物理頁面週轉要點以下圖所示:code

物理內存頁面的旅行

  • ①空閒。page結構經過list鏈入到內存管理區的空閒隊列free_area中,其使用計數count爲0;
  • ②分配。page結構從空閒隊列中脫鏈鏈,其鏈表頭list空閒,使用計數count爲1;
  • ③活躍狀態。page結構經過鏈表頭lru鏈入全局的活躍頁面隊列active_list,並經過鏈表頭list鏈入swapper_space中乾淨頁面隊列clean_pages;
  • ④不活躍髒狀態。page結構經過鏈表頭lru鏈入全局的不活躍髒頁面列表inactive_dirty_list,並經過鏈表頭list鏈入swapper_space中髒頁面列表dirty_pages。原則上再也不有任何進程的頁面表項指向該頁面,每次斷開頁面映射時都使page結構的引用計數減1;
  • ⑤不活躍乾淨狀態。將不活躍髒頁面的內容寫入磁盤頁面,內存頁面就被「洗白」了,進入不活躍乾淨狀態。page結構經過鏈表頭lru鏈入管理區的不活躍乾淨頁面隊列inactive_clean_list;
  • ⑥若是在轉入不活躍狀態一段時間內頁面受到訪問,則又轉入活躍狀態(狀態③)並恢復映射;
  • ⑦當有須要時,就從不活躍乾淨的頁面隊列中回收頁面,或者退回到空閒隊列中(圖中的狀態⑦),或者直接分配出去(圖中狀態②)

圖中涉及到結構及變量說明以下:blog

  • active_list和inactive_dirty_list是兩個全局性的LRU隊列。此外內核還在每一個內存管理區設置了一個inactive_clean_list。page經過鏈表頭lru鏈入這三個LRU隊列之一,根據page在LRU隊列中的位置,就能夠知道page的「冷卻」或「老化」程度,爲頁面回收提供依據。爲何active_list和inactive_dirty_list是全局隊列而inactive_clean_list卻率屬於某個內存管理區?緣由是系統在挑選要換出的內存頁面時「一視同仁」,不區份內存頁面所在的管理區。而當頁面進入不活躍乾淨狀態時,該頁面有可能被回收到其所在管理區的空閒隊列中,或者是再被分配出去,這兩種狀況都和內存管理區相關,因此莫不如將不活躍乾淨頁面歸到各自的內存管理區名下。
  • 由於頁面page能夠在三個LRU隊列之間轉移,因此須要page結構中設置標誌(flag字段),來表示當前在哪一個LRU隊列中,這三個標誌分別是:PG_active、PG_inactive_dirty和PG_inactive_clean。
  • 此外,內核還經過一個全局address_space結構swapper_space來管理全部可交換的內存頁面,每一個可交換的內存頁面都經過page結構中鏈表頭list鏈入其中的一個隊列。address_space結構以及全局變量swapper_space的定義以下:
<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(的地址)。進程

相關文章
相關標籤/搜索