国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 系統(tǒng) > Linux > 正文

Linux 核心--4.內(nèi)存管理

2024-07-26 00:31:28
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友
第三章 存儲(chǔ)管理

存儲(chǔ)管理子系統(tǒng)時(shí)操作系統(tǒng)中最重要的組成部分之一。在早期計(jì)算時(shí)代,由于人們所需要的內(nèi)存數(shù)目遠(yuǎn)遠(yuǎn)大于物理內(nèi)存,人們?cè)O(shè)計(jì)出了各種各樣的策略來(lái)解決此問(wèn)題,其中最成功的是虛擬內(nèi)存技術(shù)。它使得系統(tǒng)中為有限物理內(nèi)存競(jìng)爭(zhēng)的進(jìn)程所需內(nèi)存空間得到滿(mǎn)足。 

虛擬內(nèi)存技術(shù)不僅僅可讓我們可以使用更多的內(nèi)存,它還提供了以下功能: 

巨大的尋址空間 

操作系統(tǒng)讓系統(tǒng)看上去有比實(shí)際內(nèi)存大得多的內(nèi)存空間。虛擬內(nèi)存可以是系統(tǒng)中實(shí)際物理空間的許多倍。每個(gè)進(jìn)程運(yùn)行在其獨(dú)立的虛擬地址空間中。這些虛擬空間相互之間都完全隔離開(kāi)來(lái),所以進(jìn)程間不會(huì)互相影響。同時(shí),硬件虛擬內(nèi)存機(jī)構(gòu)可以將內(nèi)存的某些區(qū)域設(shè)置成不可寫(xiě)。這樣可以保護(hù)代碼與數(shù)據(jù)不會(huì)受惡意程序的干擾。 

內(nèi)存映射 

內(nèi)存映射技術(shù)可以將映象文件和數(shù)據(jù)文件直接映射到進(jìn)程的地址空間。在內(nèi)存映射中,文件的內(nèi)容被直接連接到進(jìn)程虛擬地址空間上。 

公平的物理內(nèi)存分配 

內(nèi)存管理子系統(tǒng)允許系統(tǒng)中每個(gè)運(yùn)行的進(jìn)程公平地共享系統(tǒng)中的物理內(nèi)存。 

共享虛擬內(nèi)存 

盡管虛擬內(nèi)存允許進(jìn)程有其獨(dú)立的虛擬地址空間,但有時(shí)也需要在進(jìn)程之間共享內(nèi)存。 例如有可能系統(tǒng)中有幾個(gè)進(jìn)程同時(shí)運(yùn)行BASH命令外殼程序。為了避免在每個(gè)進(jìn)程的虛擬內(nèi)存空間內(nèi)都存在BASH程序的拷貝,較好的解決辦法是系統(tǒng)物理內(nèi)存中只存在一份BASH的拷貝并在多個(gè)進(jìn)程間共享。動(dòng)態(tài)庫(kù)則是另外一種進(jìn)程間共享執(zhí)行代碼的方式。共享內(nèi)存可用來(lái)作為進(jìn)程間通訊(ipC)的手段,多個(gè)進(jìn)程通過(guò)共享內(nèi)存來(lái)交換信息。 linux支持SYSTEM V的共享內(nèi)存IPC機(jī)制。 

3.1 虛擬內(nèi)存的抽象模型



圖3.1 虛擬地址到物理地址映射的抽象模型 


在討論Linux是如何具體實(shí)現(xiàn)對(duì)虛擬內(nèi)存的支持前,有必要看一下更簡(jiǎn)單的抽象模型。 

在處理器執(zhí)行程序時(shí)需要將其從內(nèi)存中讀出再進(jìn)行指令解碼。在指令解碼之前它必須向內(nèi)存中某個(gè)位置取出或者存入某個(gè)值。然后執(zhí)行此指令并指向程序中下一條指令。在此過(guò)程中處理器必須頻繁訪(fǎng)問(wèn)內(nèi)存,要么取指取數(shù),要么存儲(chǔ)數(shù)據(jù)。 

虛擬內(nèi)存系統(tǒng)中的所有地址都是虛擬地址而不是物理地址。通過(guò)操作系統(tǒng)所維護(hù)的一系列表格由處理器實(shí)現(xiàn)由虛擬地址到物理地址的轉(zhuǎn)換。 

為了使轉(zhuǎn)換更加簡(jiǎn)單,虛擬內(nèi)存與物理內(nèi)存都以頁(yè)面來(lái)組織。不同系統(tǒng)中頁(yè)面的大小可以相同,也可以不同,這樣將帶來(lái)管理的不便。Alpha AXP處理器上運(yùn)行的Linux頁(yè)面大小為8KB,而Intel X86系統(tǒng)上使用4KB頁(yè)面。每個(gè)頁(yè)面通過(guò)一個(gè)叫頁(yè)面框號(hào)的數(shù)字來(lái)標(biāo)示(PFN) 。 

頁(yè)面模式下的虛擬地址由兩部分構(gòu)成:頁(yè)面框號(hào)和頁(yè)面內(nèi)偏移值。如果頁(yè)面大小為4KB,則虛擬地址的 11:0位表示虛擬地址偏移值,12位以上表示虛擬頁(yè)面框號(hào)。處理器處理虛擬地址時(shí)必須完成地址分離工作。在頁(yè)表的幫助下,它將虛擬頁(yè)面框號(hào)轉(zhuǎn)換成物理頁(yè)面框號(hào),然后訪(fǎng)問(wèn)物理頁(yè)面中相應(yīng)偏移處。 

圖3.1給出了兩個(gè)進(jìn)程X和Y的虛擬地址空間,它們擁有各自的頁(yè)表。這些頁(yè)表將各個(gè)進(jìn)程的虛擬頁(yè)面映射到內(nèi)存中的物理頁(yè)面。在圖中,進(jìn)程X的虛擬頁(yè)面框號(hào)0被映射到了物理頁(yè)面框號(hào)4。理論上每個(gè)頁(yè)表入口應(yīng)包含以下內(nèi)容: 


有效標(biāo)記,表示此頁(yè)表入口是有效的 
頁(yè)表入口描敘的物理頁(yè)面框號(hào) 
訪(fǎng)問(wèn)控制信息。用來(lái)描敘此頁(yè)可以進(jìn)行哪些操作,是否可寫(xiě)?是否包含執(zhí)行代碼? 
虛擬頁(yè)面框號(hào)是為頁(yè)表中的偏移。虛擬頁(yè)面框號(hào)5對(duì)應(yīng)表中的第6個(gè)單元(0是第一個(gè))。 

為了將虛擬地址轉(zhuǎn)換為物理地址,處理器首先必須得到虛擬地址頁(yè)面框號(hào)及頁(yè)內(nèi)偏移。一般將頁(yè)面大小設(shè)為2的次冪。將圖3.1中的頁(yè)面大小設(shè)為0x2000字節(jié)(十進(jìn)制為8192)并且在進(jìn)程Y的虛擬地址空間中某個(gè)地址為0x2194,則處理器將其轉(zhuǎn)換為虛擬頁(yè)面框號(hào)1及頁(yè)內(nèi)偏移0x194。 

處理器使用虛擬頁(yè)面框號(hào)為索引來(lái)訪(fǎng)問(wèn)處理器頁(yè)表,檢索頁(yè)表入口。如果在此位置的頁(yè)表入口有效,則處理器將從此入口中得到物理頁(yè)面框號(hào)。如果此入口無(wú)效,則意味著處理器存取的是虛擬內(nèi)存中一個(gè)不存在的區(qū)域。在這種情況下,處理器是不能進(jìn)行地址轉(zhuǎn)換的,它必須將控制傳遞給操作系統(tǒng)來(lái)完成這個(gè)工作。 

某個(gè)進(jìn)程試圖訪(fǎng)問(wèn)處理器無(wú)法進(jìn)行有效地址轉(zhuǎn)換的虛擬地址時(shí),處理器如何將控制傳遞到操作系統(tǒng)依賴(lài)于具體的處理器。通常的做法是:處理器引發(fā)一個(gè)頁(yè)面失效錯(cuò)而陷入操作系統(tǒng)核心,這樣操作系統(tǒng)將得到有關(guān)無(wú)效虛擬地址的信息以及發(fā)生頁(yè)面錯(cuò)誤的原因。 

再以圖3.1為例,進(jìn)程Y的虛擬頁(yè)面框號(hào)1被映射到系統(tǒng)物理頁(yè)面框號(hào)4,則再物理內(nèi)存中的起始位置為 0x8000(4 * 0x2000)。加上0x194字節(jié)偏移則得到最終的物理地址0x8194。 

通過(guò)將虛擬地址映射到物理地址,虛擬內(nèi)存可以以任何順序映射到系統(tǒng)物理頁(yè)面。例如,在圖3.1中,進(jìn)程X的虛擬頁(yè)面框號(hào)0被映射到物理頁(yè)面框號(hào)1而虛擬頁(yè)面框號(hào)7被映射到物理頁(yè)面框號(hào)0,雖然后者的虛擬頁(yè)面框號(hào)要高于前者。這樣虛擬內(nèi)存技術(shù)帶來(lái)了有趣的結(jié)果:虛擬內(nèi)存中的頁(yè)面無(wú)須在物理內(nèi)存保持特定順序。 

  

3.1.1 請(qǐng)求換頁(yè)
在物理內(nèi)存比虛擬內(nèi)存小得多的系統(tǒng)中,操作系統(tǒng)必須提高物理內(nèi)存的使用效率。節(jié)省物理內(nèi)存的一種方法是僅加載那些正在被執(zhí)行程序使用的虛擬頁(yè)面。比如說(shuō),某個(gè)數(shù)據(jù)庫(kù)程序可能要對(duì)某個(gè)數(shù)據(jù)庫(kù)進(jìn)行查詢(xún)操作,此時(shí)并不是數(shù)據(jù)庫(kù)的所有內(nèi)容都要加載到內(nèi)存中去,而只加載那些要用的部分。如果此數(shù)據(jù)庫(kù)查詢(xún)是一個(gè)搜索查詢(xún)而無(wú)須對(duì)數(shù)據(jù)庫(kù)進(jìn)行添加記錄操作,則加載添加記錄的代碼是毫無(wú)意義的。這種僅將要訪(fǎng)問(wèn)的虛擬頁(yè)面載入的技術(shù)叫請(qǐng)求換頁(yè)。 

當(dāng)進(jìn)程試圖訪(fǎng)問(wèn)當(dāng)前不在內(nèi)存中的虛擬地址時(shí),處理器在頁(yè)表中無(wú)法找到所引用地址的入口。在圖3.1中,對(duì)于虛擬頁(yè)面框號(hào)2,進(jìn)程X的頁(yè)表中沒(méi)有入口,這樣當(dāng)進(jìn)程X試圖訪(fǎng)問(wèn)虛擬頁(yè)面框號(hào)2內(nèi)容時(shí),處理器不能將此地址轉(zhuǎn)換成物理地址。這時(shí)處理器通知操作系統(tǒng)有頁(yè)面錯(cuò)誤發(fā)生。 

如果發(fā)生頁(yè)面錯(cuò)的虛擬地址是無(wú)效的,則表明進(jìn)程在試圖訪(fǎng)問(wèn)一個(gè)不存在的虛擬地址。這可能是應(yīng)用程序出錯(cuò)而引起的,例如它試圖對(duì)內(nèi)存進(jìn)行一個(gè)隨機(jī)的寫(xiě)操作。此時(shí)操作系統(tǒng)將終止此應(yīng)用的運(yùn)行以保護(hù)系統(tǒng)中其他進(jìn)程不受此出錯(cuò)進(jìn)程的影響。 

如果出錯(cuò)虛擬地址是有效的,但是它指向的頁(yè)面當(dāng)前不在內(nèi)存中,則操作系統(tǒng)必須將此頁(yè)面從磁盤(pán)映象中讀入到內(nèi)存中來(lái)。由于訪(fǎng)盤(pán)時(shí)間較長(zhǎng),進(jìn)程必須等待一段時(shí)間直到頁(yè)面被取出來(lái)。如果系統(tǒng)中還存在其他進(jìn)程,操作系統(tǒng)就會(huì)在讀取頁(yè)面過(guò)程中的等待過(guò)程中選擇其中之一來(lái)運(yùn)行。讀取回來(lái)的頁(yè)面將被放在一個(gè)空閑的物理頁(yè)面框中,同時(shí)此進(jìn)程的頁(yè)表中將添加對(duì)應(yīng)此虛擬頁(yè)面框號(hào)的入口。最后進(jìn)程將從發(fā)生頁(yè)面錯(cuò)誤的地方重新開(kāi)始運(yùn)行。此時(shí)整個(gè)虛擬內(nèi)存訪(fǎng)問(wèn)過(guò)程告一段落,處理器又可以繼續(xù)進(jìn)行虛擬地址到物理地址轉(zhuǎn)換,而進(jìn)程也得以繼續(xù)運(yùn)行。 

Linux使用請(qǐng)求換頁(yè)將可執(zhí)行映象加載到進(jìn)程的虛擬內(nèi)存中。當(dāng)命令執(zhí)行時(shí),可執(zhí)行的命令文件被打開(kāi),同時(shí)其內(nèi)容被映射到進(jìn)程的虛擬內(nèi)存。這些操作是通過(guò)修改描敘進(jìn)程內(nèi)存映象的數(shù)據(jù)結(jié)構(gòu)來(lái)完成的,此過(guò)程稱(chēng)為內(nèi)存映射。然而只有映象的起始部分被調(diào)入物理內(nèi)存,其余部分仍然留在磁盤(pán)上。當(dāng)映象執(zhí)行時(shí),它會(huì)產(chǎn)生頁(yè)面錯(cuò)誤,這樣Linux將決定將磁盤(pán)上哪些部分調(diào)入內(nèi)存繼續(xù)執(zhí)行。 

3.1.2 交換 
如果進(jìn)程需要把一個(gè)虛擬頁(yè)面調(diào)入物理內(nèi)存而正好系統(tǒng)中沒(méi)有空閑的物理頁(yè)面,操作系統(tǒng)必須丟棄位于物理內(nèi)存中的某些頁(yè)面來(lái)為之騰出空間。 

如果那些從物理內(nèi)存中丟棄出來(lái)的頁(yè)面來(lái)自于磁盤(pán)上的可執(zhí)行文件或者數(shù)據(jù)文件,并且沒(méi)有修改過(guò)則不需要保存那些頁(yè)面。當(dāng)進(jìn)程再次需要此頁(yè)面時(shí),直接從可執(zhí)行文件或者數(shù)據(jù)文件中讀出。 

但是如果頁(yè)面被修改過(guò),則操作系統(tǒng)必須保留頁(yè)面的內(nèi)容以備再次訪(fǎng)問(wèn)。這種頁(yè)面被稱(chēng)為dirty頁(yè)面, 當(dāng)從內(nèi)存中移出來(lái)時(shí),它們必須保存在叫做交換文件的特殊文件中。相對(duì)于處理器和物理內(nèi)存的速度,訪(fǎng)問(wèn)交換文件的速度是非常緩慢的,操作系統(tǒng)必須在將這些dirty頁(yè)面寫(xiě)入磁盤(pán)和將其繼續(xù)保留在內(nèi)存中做出選擇。 

選擇丟棄頁(yè)面的算法經(jīng)常需要判斷哪些頁(yè)面要丟棄或者交換,如果交換算法效率很低,則會(huì)發(fā)生"顛簸"現(xiàn)象。在這種情況下,頁(yè)面不斷的被寫(xiě)入磁盤(pán)又從磁盤(pán)中讀回來(lái),這樣一來(lái)操作系統(tǒng)就無(wú)法進(jìn)行其他任何工作。以圖3.1為例,如果物理頁(yè)面框號(hào)1被頻繁使用,則頁(yè)面丟棄算法將其作為交換到硬盤(pán)的侯選者是不恰當(dāng)?shù)摹R粋€(gè)進(jìn)程當(dāng)前經(jīng)常使用的頁(yè)面集合叫做工作集。高效的交換策略能夠確保所有進(jìn)程的工作集保存在物理內(nèi)存中。 

Linux使用最近最少使用(LRU)頁(yè)面衰老算法來(lái)公平地選擇將要從系統(tǒng)中拋棄的頁(yè)面。這種策略為系統(tǒng)中的每個(gè)頁(yè)面設(shè)置一個(gè)年齡,它隨頁(yè)面訪(fǎng)問(wèn)次數(shù)而變化。頁(yè)面被訪(fǎng)問(wèn)的次數(shù)越多則頁(yè)面年齡越年輕;相反則越衰老。年齡較老的頁(yè)面是待交換頁(yè)面的最佳侯選者。 

  

3.1.3  共享虛擬內(nèi)存
虛擬內(nèi)存讓多個(gè)進(jìn)程之間可以方便地共享內(nèi)存。所有的內(nèi)存訪(fǎng)問(wèn)都是通過(guò)每個(gè)進(jìn)程自身的頁(yè)表進(jìn)行。對(duì)于兩個(gè)共享同一物理頁(yè)面的進(jìn)程,在各自的頁(yè)表中必須包含有指向這一物理頁(yè)面框號(hào)的頁(yè)表入口。 

圖3.1中兩個(gè)進(jìn)程共享物理頁(yè)面框號(hào)4。對(duì)進(jìn)程X來(lái)說(shuō)其對(duì)應(yīng)的虛擬頁(yè)面框號(hào)為4而進(jìn)程Y的為6。這個(gè)有趣的現(xiàn)象說(shuō)明:共享物理頁(yè)面的進(jìn)程對(duì)應(yīng)此頁(yè)面的虛擬內(nèi)存位置可以不同。 

  

3.1.4  物理與虛擬尋址模式
操作系統(tǒng)自身也運(yùn)行在虛擬內(nèi)存中的意義不大。如果操作系統(tǒng)被迫維護(hù)自身的頁(yè)表那將是一個(gè)令人惡心的方案。多數(shù)通用處理器同時(shí)支持物理尋址和虛擬尋址模式。物理尋址模式無(wú)需頁(yè)表的參與且處理器不會(huì)進(jìn)行任何地址轉(zhuǎn)換。Linux核心直接運(yùn)行在物理地址空間上。 

Alpha AXP處理器沒(méi)有特殊的物理尋址模式。它將內(nèi)存空間劃分為幾個(gè)區(qū)域并將其中兩個(gè)指定為物理映射地址。核心地址空間被稱(chēng)為KSEG地址空間,它位于地址0xfffffc0000000000以上區(qū)域。為了執(zhí)行位于KSEG的核心代碼或訪(fǎng)問(wèn)那里的數(shù)據(jù),代碼必須在核心模式下執(zhí)行。Alpha上的Linux核心從地址0xfffffc0000310000開(kāi)始執(zhí)行. 

  

3.1.5  訪(fǎng)問(wèn)控制
頁(yè)表入口包含了訪(fǎng)問(wèn)控制信息。由于處理器已經(jīng)將頁(yè)表入口作為虛擬地址到物理地址的映射,那么可以很方便地使用訪(fǎng)問(wèn)控制信息來(lái)判斷處理器是否在以其應(yīng)有的方式來(lái)訪(fǎng)問(wèn)內(nèi)存。 

諸多因素使得有必要嚴(yán)格控制對(duì)內(nèi)存區(qū)域的訪(fǎng)問(wèn)。有些內(nèi)存,如包含執(zhí)行代碼的部分,顯然應(yīng)該是只讀的,操作系統(tǒng)決不能允許進(jìn)程對(duì)此區(qū)域的寫(xiě)操作。相反包含數(shù)據(jù)的頁(yè)面應(yīng)該是可寫(xiě)的, 但是去執(zhí)行這段數(shù)據(jù)肯定將導(dǎo)致錯(cuò)誤發(fā)生。多數(shù)處理器至少有兩種執(zhí)行方式:核心態(tài)與用戶(hù)態(tài)。任何人都不會(huì)允許在用戶(hù)態(tài)下執(zhí)行核心代碼或者在用戶(hù)態(tài)下修改核心數(shù)據(jù)結(jié)構(gòu)。 

  



圖3.2 Alpha AXP頁(yè)表入口 


頁(yè)表入口中的訪(fǎng)問(wèn)控制信息是處理器相關(guān)的;圖3.2是Alpha AXP處理器的PTE(Page Table Entry)。這些位域的含義如下: 


V 
有效,如果此位置位,表明此PTE有效 
  
FOE 
“執(zhí)行時(shí)失效”,無(wú)論合時(shí)只要執(zhí)行包含在此頁(yè)面中的指令,處理器都將報(bào)告頁(yè)面錯(cuò)誤并將控制傳遞 
FOW 
“寫(xiě)時(shí)失效”, 除了頁(yè)面錯(cuò)誤發(fā)生在對(duì)此頁(yè)面的寫(xiě)時(shí),其他與上相同。 
FOR 
“讀時(shí)失效”,除了頁(yè)面錯(cuò)誤發(fā)生在對(duì)此頁(yè)面的讀時(shí),其他與上相同。 
ASM 
地址空間匹配。被操作系統(tǒng)用于清洗轉(zhuǎn)換緩沖中的某些入口。 
KRE 
運(yùn)行在核心模式下的代碼可以讀此頁(yè)面。 
URE 
運(yùn)行在用戶(hù)模式下的代碼可以讀此頁(yè)面。 
GH 
將整個(gè)塊映射到單個(gè)而不是多個(gè)轉(zhuǎn)換緩沖時(shí)的隱含粒度。 
KWE 
運(yùn)行在核心模式下的代碼可以寫(xiě)此頁(yè)面。 
UWE 
運(yùn)行在用戶(hù)模式下的代碼可以寫(xiě)此頁(yè)面。 
page frame number 
對(duì)于V位置位的PTE,此域包含了對(duì)應(yīng)此PTE的物理頁(yè)面框號(hào);對(duì)于無(wú)效PTE,此域不為0,它包含了頁(yè)面在交換文件中位置的信息。 
以下兩位由Linux定義并使用。 


_PAGE_DIRTY 
如果置位,此頁(yè)面要被寫(xiě)入交換文件。 
_PAGE_accessED 
Linux用它表示頁(yè)面已經(jīng)被訪(fǎng)問(wèn)過(guò)。 

3.2  高速緩沖
如果用上述理論模型來(lái)實(shí)現(xiàn)一個(gè)系統(tǒng),它可能可以工作,但效率不會(huì)高。操作系統(tǒng)設(shè)計(jì)者和處理器設(shè)計(jì)者都在努力以提高系統(tǒng)的性能。除了制造更快的CPU和內(nèi)存外,最好的辦法是在高速緩沖中維護(hù)有用信息和數(shù)據(jù)以加快某些操作。Linux使用了許多與高速緩沖相關(guān)的內(nèi)存管理策略。 
Buffer Cache 
這個(gè)buffer cache中包含了被塊設(shè)備驅(qū)動(dòng)使用的數(shù)據(jù)緩沖。 

這些緩沖的單元的大小一般固定(例如說(shuō)512字節(jié))并且包含從塊設(shè)備讀出或者寫(xiě)入的信息塊。塊設(shè)備是僅能夠以固定大小塊進(jìn)行讀寫(xiě)操作的設(shè)備。所有的硬盤(pán)都是塊設(shè)備。 

  
利用設(shè)備標(biāo)志符和所需塊號(hào)作索引可以在buffer cache中迅速地找到數(shù)據(jù)。塊設(shè)備只能夠通過(guò)buffer cache來(lái)存取。如果數(shù)據(jù)在buffer cache中可以找到則無(wú)需從物理塊設(shè)備(如硬盤(pán))中讀取,這樣可以加速訪(fǎng)問(wèn)。 

Page Cache

用來(lái)加速硬盤(pán)上可執(zhí)行映象文件與數(shù)據(jù)文件的存取。 

它每次緩沖一個(gè)頁(yè)面的文件內(nèi)容。頁(yè)面從磁盤(pán)上讀入內(nèi)存后緩存在page cache中。 

  
  

Swap Cache 
只有修改過(guò)的頁(yè)面存儲(chǔ)在交換文件中。 

只要這些頁(yè)面在寫(xiě)入到交換文件后沒(méi)有被修改,則下次此頁(yè)面被交換出內(nèi)存時(shí),就不必再進(jìn)行更新寫(xiě)操作,這些頁(yè)面都可以簡(jiǎn)單的丟棄。在交換頻繁發(fā)生的系統(tǒng)中,Swap Cache可以省下很多不必要且耗時(shí)的磁盤(pán)操作。 

  
  

Hardware Caches 
一個(gè)常見(jiàn)的hardware cache是處理器中的頁(yè)表入口cache。處理器不總是直接讀取頁(yè)表而是在需要時(shí)緩存頁(yè)面的轉(zhuǎn)換。這種cache又叫做轉(zhuǎn)換旁視緩沖(Translation Look-aside Buffers),它包含系統(tǒng)中一個(gè)或多個(gè)處理器的頁(yè)表入口的緩沖拷貝。 

  
當(dāng)發(fā)出對(duì)虛擬地址的引用時(shí),處理器試圖找到相匹配的TLB入口。如果找到則直接將虛擬地址轉(zhuǎn)換成物理地址并對(duì)數(shù)據(jù)進(jìn)行處理。如果沒(méi)有找到則向操作系統(tǒng)尋求幫助。處理器將向操作系統(tǒng)發(fā)出TLB失配信號(hào),它使用一個(gè)特定的系統(tǒng)機(jī)制來(lái)將此異常通知操作系統(tǒng)。操作系統(tǒng)則為此地址匹配對(duì)產(chǎn)生新的TLB入口。當(dāng)操作系統(tǒng)清除此異常時(shí),處理器將再次進(jìn)行虛擬地址轉(zhuǎn)換。由于此時(shí)在TLB中已經(jīng)有相應(yīng)的入口,這次操作將成功。 

  
使用高速緩存的缺點(diǎn)在于Linux必須消耗更多的時(shí)間和空間來(lái)維護(hù)這些緩存,并且當(dāng)緩存系統(tǒng)崩潰時(shí)系統(tǒng)也將崩潰。 


3.3  Linux 頁(yè)表



圖3.3 Linux的三級(jí)頁(yè)表結(jié)構(gòu) 


Linux總是假定處理器有三級(jí)頁(yè)表。每個(gè)頁(yè)表通過(guò)所包含的下級(jí)頁(yè)表的頁(yè)面框號(hào)來(lái)訪(fǎng)問(wèn)。圖3.3給出了虛擬地址是如何分割成多個(gè)域的,每個(gè)域提供了某個(gè)指定頁(yè)表的偏移。為了將虛擬地址轉(zhuǎn)換成物理地址,處理器必須得到每個(gè)域的值。這個(gè)過(guò)程將持續(xù)三次直到對(duì)應(yīng)于虛擬地址的物理頁(yè)面框號(hào)被找到。最后再使用虛擬地址中的最后一個(gè)域,得到了頁(yè)面中數(shù)據(jù)的地址。 

為了實(shí)現(xiàn)跨平臺(tái)運(yùn)行,Linux提供了一系列轉(zhuǎn)換宏使得核心可以訪(fǎng)問(wèn)特定進(jìn)程的頁(yè)表。這樣核心無(wú)需知道 頁(yè)表入口的結(jié)構(gòu)以及它們的排列方式。 

這種策略相當(dāng)成功,無(wú)論在具有三級(jí)頁(yè)表結(jié)構(gòu)的Alpha AXP還是兩級(jí)頁(yè)表的Intel X86處理器中,Linux總是使 用相同的頁(yè)表操縱代碼。 

3.4  頁(yè)面分配與回收
對(duì)系統(tǒng)中物理頁(yè)面的請(qǐng)求十分頻繁。例如當(dāng)一個(gè)可執(zhí)行映象被調(diào)入內(nèi)存時(shí),操作系統(tǒng)必須為其分配頁(yè)面。當(dāng)映象執(zhí)行完畢和卸載時(shí)這些頁(yè)面必須被釋放。物理頁(yè)面的另一個(gè)用途是存儲(chǔ)頁(yè)表這些核心數(shù)據(jù)結(jié)構(gòu)。虛擬內(nèi)存子系統(tǒng)中負(fù)責(zé)頁(yè)面分配與回收的數(shù)據(jù)結(jié)構(gòu)和機(jī)制可能用處最大。 

系統(tǒng)中所有的物理頁(yè)面用包含mem_map_t結(jié)構(gòu)的鏈表mem_map來(lái)描敘,這些結(jié)構(gòu)在系統(tǒng)啟動(dòng)時(shí)初始化。每個(gè) mem_map_t描敘了一個(gè)物理頁(yè)面。其中與內(nèi)存管理相關(guān)的重要域如下: 

count 

記錄使用此頁(yè)面的用戶(hù)個(gè)數(shù)。當(dāng)這個(gè)頁(yè)面在多個(gè)進(jìn)程之間共享時(shí),它的值大于1。 
age 
此域描敘頁(yè)面的年齡,用于選擇將適當(dāng)?shù)捻?yè)面拋棄或者置換出內(nèi)存時(shí)。 
map_nr 
記錄本mem_map_t描敘的物理頁(yè)面框號(hào)。 
頁(yè)面分配代碼使用free_area數(shù)組來(lái)尋找和釋放頁(yè)面,此機(jī)制負(fù)責(zé)整個(gè)緩沖管理。另外此代碼與處理器使用的頁(yè)面大小和物理分頁(yè)機(jī)制無(wú)關(guān)。 

free_area中的每個(gè)元素都包含頁(yè)面塊的信息。數(shù)組中第一個(gè)元素描敘1個(gè)頁(yè)面,第二個(gè)表示2個(gè)頁(yè)面大小的塊而接下來(lái)表示4個(gè)頁(yè)面大小的塊,總之都是2的次冪倍大小。list域表示一個(gè)隊(duì)列頭,它包含指向mem_map數(shù)組中page數(shù)據(jù)結(jié)構(gòu)的指針。所有的空閑頁(yè)面都在此隊(duì)列中。map域是指向某個(gè)特定頁(yè)面尺寸的頁(yè)面組分配情況位圖的指針。當(dāng)頁(yè)面的第N塊空閑時(shí),位圖的第N位被置位。 

圖free-area-figure畫(huà)出了free_area結(jié)構(gòu)。第一個(gè)元素有個(gè)自由頁(yè)面(頁(yè)面框號(hào)0),第二個(gè)元素有4個(gè)頁(yè)面大小的2個(gè)自由塊,前一個(gè)從頁(yè)面框號(hào)4開(kāi)始而后一個(gè)從頁(yè)面框號(hào)56開(kāi)始。 

  

3.4.1  頁(yè)面分配
Linux使用Buddy算法來(lái)有效的分配與回收頁(yè)面塊。頁(yè)面分配代碼每次分配包含一個(gè)或者多個(gè)物理頁(yè)面的內(nèi)存塊。頁(yè)面以2的次冪的內(nèi)存塊來(lái)分配。這意味著它可以分配1個(gè)、2個(gè)和4個(gè)頁(yè)面的塊。只要系統(tǒng)中有足夠的空閑頁(yè)面來(lái)滿(mǎn)足這個(gè)要求(nr_free_pages > min_free_page),內(nèi)存分配代碼將在free_area中尋找一個(gè)與請(qǐng)求大小相同的空閑塊。free_area中的每個(gè)元素保存著一個(gè)反映這樣大小的已分配與空閑頁(yè)面 的位圖。例如,free_area數(shù)組中第二個(gè)元素指向一個(gè)反映大小為四個(gè)頁(yè)面的內(nèi)存塊分配情況的內(nèi)存映象。 

分配算法首先搜尋滿(mǎn)足請(qǐng)求大小的頁(yè)面。它從free_area數(shù)據(jù)結(jié)構(gòu)的list域著手沿鏈來(lái)搜索空閑頁(yè)面。如果沒(méi)有這樣請(qǐng)求大小的空閑頁(yè)面,則它搜索兩倍于請(qǐng)求大小的內(nèi)存塊。這個(gè)過(guò)程一直將持續(xù)到free_area 被搜索完或找到滿(mǎn)足要求的內(nèi)存塊為止。如果找到的頁(yè)面塊大于請(qǐng)求的塊則對(duì)其進(jìn)行分割以使其大小與請(qǐng)求塊匹配。由于塊大小都是2的次冪所以分割過(guò)程十分簡(jiǎn)單。空閑塊被連進(jìn)相應(yīng)的隊(duì)列而這個(gè)頁(yè)面塊被分配給調(diào)用者。 





圖3.4 free_area數(shù)據(jù)結(jié)構(gòu) 

在圖3.4中,當(dāng)系統(tǒng)中有大小為兩個(gè)頁(yè)面塊的請(qǐng)求發(fā)出時(shí),第一個(gè)4頁(yè)面大小的內(nèi)存塊(從頁(yè)面框號(hào)4開(kāi)始)將分成兩個(gè)2頁(yè)面大小的塊。前一個(gè),從頁(yè)面框號(hào)4開(kāi)始的,將分配出去返回給請(qǐng)求者,而后一個(gè),從頁(yè)面框號(hào)6開(kāi)始,將被添加到free_area數(shù)組中表示兩個(gè)頁(yè)面大小的空閑塊的元素1中。 

3.4.2  頁(yè)面回收
將大的頁(yè)面塊打碎進(jìn)行分配將增加系統(tǒng)中零碎空閑頁(yè)面塊的數(shù)目。頁(yè)面回收代碼在適當(dāng)時(shí)機(jī)下要將這些頁(yè)面結(jié)合起來(lái)形成單一大頁(yè)面塊。事實(shí)上頁(yè)面塊大小決定了頁(yè)面重新組合的難易程度。 

當(dāng)頁(yè)面塊被釋放時(shí),代碼將檢查是否有相同大小的相鄰或者buddy內(nèi)存塊存在。如果有,則將它們結(jié)合起來(lái)形成一個(gè)大小為原來(lái)兩倍的新空閑塊。每次結(jié)合完之后,代碼還要檢查是否可以繼續(xù)合并成更大的頁(yè)面。最佳情況是系統(tǒng)的空閑頁(yè)面塊將和允許分配的最大內(nèi)存一樣大。 

在圖3.4中,如果釋放頁(yè)面框號(hào)1,它將和空閑頁(yè)面框號(hào)0結(jié)合作為大小為2個(gè)頁(yè)面的空閑塊排入free_area的第一個(gè)元素中。 

3.5  內(nèi)存映射
映象執(zhí)行時(shí),可執(zhí)行映象的內(nèi)容將被調(diào)入進(jìn)程虛擬地址空間中。可執(zhí)行映象使用的共享庫(kù)同樣如此。然而可執(zhí)行文件實(shí)際上并沒(méi)有調(diào)入物理內(nèi)存,而是僅僅連接到進(jìn)程的虛擬內(nèi)存。當(dāng)程序的其他部分運(yùn)行時(shí)引用到這部分時(shí)才把它們從磁盤(pán)上調(diào)入內(nèi)存。將映象連接到進(jìn)程虛擬地址空間的過(guò)程稱(chēng)為內(nèi)存映射。 



圖3.5 虛擬內(nèi)存區(qū)域 


每個(gè)進(jìn)程的虛擬內(nèi)存用一個(gè)mm_struct來(lái)表示。它包含當(dāng)前執(zhí)行的映象(如BASH)以及指向vm_area_struct 的大量指針。每個(gè)vm_area_struct數(shù)據(jù)結(jié)構(gòu)描敘了虛擬內(nèi)存的起始與結(jié)束位置,進(jìn)程對(duì)此內(nèi)存區(qū)域的存取權(quán)限以及一組內(nèi)存操作函數(shù)。這些函數(shù)都是Linux在操縱虛擬內(nèi)存區(qū)域時(shí)必須用到的子程序。其中一個(gè)負(fù)責(zé)處理進(jìn)程試圖訪(fǎng)問(wèn)不在當(dāng)前物理內(nèi)存中的虛擬內(nèi)存(通過(guò)頁(yè)面失效)的情況。此函數(shù)叫nopage。它用在Linux試圖將可執(zhí)行映象的頁(yè)面調(diào)入內(nèi)存時(shí)。 

可執(zhí)行映象映射到進(jìn)程虛擬地址時(shí)將產(chǎn)生一組相應(yīng)的vm_area_struct數(shù)據(jù)結(jié)構(gòu)。每個(gè)vm_area_struct數(shù)據(jù)結(jié)構(gòu)表示可執(zhí)行映象的一部分:可執(zhí)行代碼、初始化數(shù)據(jù)(變量)、未初始化數(shù)據(jù)等等。Linux支持許多標(biāo)準(zhǔn)的虛擬內(nèi)存操作函數(shù),創(chuàng)建vm_area_struct數(shù)據(jù)結(jié)構(gòu)時(shí)有一組相應(yīng)的虛擬內(nèi)存操作函數(shù)與之對(duì)應(yīng)。 

3.6  請(qǐng)求換頁(yè)
當(dāng)可執(zhí)行映象到進(jìn)程虛擬地址空間的映射完成后,它就可以開(kāi)始運(yùn)行了。由于只有很少部分的映象調(diào)入內(nèi)存,所以很快就會(huì)發(fā)生對(duì)不在物理內(nèi)存中的虛擬內(nèi)存區(qū)域的訪(fǎng)問(wèn)。當(dāng)進(jìn)程訪(fǎng)問(wèn)無(wú)有效頁(yè)表入口的虛擬地址時(shí),處理器將向Linux報(bào)告一個(gè)頁(yè)面錯(cuò)誤。 

頁(yè)面錯(cuò)誤帶有失效發(fā)生的虛擬地址及引發(fā)失效的訪(fǎng)存方式。Linux必須找到表示此區(qū)域的vm_area_struct結(jié)構(gòu)。對(duì)vm_area_struct數(shù)據(jù)結(jié)構(gòu)的搜尋速度決定了處理頁(yè)面錯(cuò)誤的效率,而所有vm_area_struct結(jié)構(gòu)是通過(guò)一種AVL(Adelson-Velskii and Landis) 樹(shù)結(jié)構(gòu)連在一起的。如果無(wú)法找到vm_area_struct與此失效虛擬地址的對(duì)應(yīng)關(guān)系,則系統(tǒng)認(rèn)為此進(jìn)程訪(fǎng)問(wèn)了非法虛擬地址。這時(shí)Linux將向進(jìn)程發(fā)送SIGSEGV信號(hào),如果進(jìn)程沒(méi)有此信號(hào)的處理過(guò)程則終止運(yùn)行。 

如果找到此對(duì)應(yīng)關(guān)系,Linux接下來(lái)檢查引起該頁(yè)面錯(cuò)誤的訪(fǎng)存類(lèi)型。如果進(jìn)程以非法方式訪(fǎng)問(wèn)內(nèi)存,比如對(duì)不可寫(xiě)區(qū)域進(jìn)行寫(xiě)操作,系統(tǒng)將產(chǎn)生內(nèi)存錯(cuò)誤的信號(hào)。 

如果Linux認(rèn)為頁(yè)面出錯(cuò)是合法的,那么它需要對(duì)這種情況進(jìn)行處理。 

首先Linux必須區(qū)分位于交換文件中的頁(yè)面和那些位于磁盤(pán)上的可執(zhí)行映象。Alpha AXP的頁(yè)表中有可能存在有效位沒(méi)有設(shè)置但是在PFN域中有非0值的頁(yè)表入口。在這種情況下,PFN域指示的是此頁(yè)面在交換文件中的位置。如何處理交換文件中的頁(yè)面將在下章討論。 

不是所有的vm_area_struct數(shù)據(jù)結(jié)構(gòu)都有一組虛擬內(nèi)存操作函數(shù),它們有的甚至沒(méi)有nopage函數(shù)。這是因?yàn)椤inux通過(guò)分配新的物理頁(yè)面并為其創(chuàng)建有效的頁(yè)表入口來(lái)修正這次訪(fǎng)問(wèn)。如果這個(gè)內(nèi)存區(qū)域存在nopage操作函數(shù),Linux將調(diào)用它。 

一般Linux nopage函數(shù)被用來(lái)處理內(nèi)存映射可執(zhí)行映象,同時(shí)它使用頁(yè)面cache將請(qǐng)求的頁(yè)面調(diào)入物理內(nèi)存中去。 

當(dāng)請(qǐng)求的頁(yè)面調(diào)入物理內(nèi)存時(shí),處理器頁(yè)表也必須更新。更新這些入口必須進(jìn)行相關(guān)硬件操作,特別是處理器使用TLB時(shí)。這樣當(dāng)頁(yè)面失效被處理完畢后,進(jìn)程將從發(fā)生失效虛擬內(nèi)存訪(fǎng)問(wèn)的位置重新開(kāi)始運(yùn)行。 

3.7  Linux頁(yè)面cache



圖3.6 Linux頁(yè)面Cache 


Linux使用頁(yè)面cache的目的是加快對(duì)磁盤(pán)上文件的訪(fǎng)問(wèn)。內(nèi)存映射文件以每次一頁(yè)的方式讀出并將這些頁(yè)面存儲(chǔ)在頁(yè)面cache中。圖3.6表明頁(yè)面cache由page_hash_table,指向mem_map_t數(shù)據(jù)結(jié)構(gòu)的指針數(shù)組組成。 

Linux中的每個(gè)文件通過(guò)一個(gè)VFS inode(在文件系統(tǒng)一章中講敘)數(shù)據(jù)結(jié)構(gòu)來(lái)標(biāo)識(shí)并且每個(gè)VFS inode都是唯一的,它可以并僅可以描敘一個(gè)文件。頁(yè)表的索引從文件的VFS inode和文件的偏移中派生出來(lái)。 

從一個(gè)內(nèi)存映射文件中讀出頁(yè)面,例如產(chǎn)生換頁(yè)請(qǐng)求時(shí)要將頁(yè)面讀回內(nèi)存中,系統(tǒng)嘗試從頁(yè)面cache來(lái)讀出。如果頁(yè)面在cache中,則返回頁(yè)面失效處理過(guò)程一個(gè)指向mem_map_t數(shù)據(jù)結(jié)構(gòu);否則此頁(yè)面將從包含映象的文件系統(tǒng)中讀入內(nèi)存并為之分配物理頁(yè)面。 

在映象的讀入與執(zhí)行過(guò)程中,頁(yè)面cache不斷增長(zhǎng)。當(dāng)不再需要某個(gè)頁(yè)面時(shí),即不再被任何進(jìn)程使用時(shí),它將被從頁(yè)面cache中刪除。 

3.8  換出與丟棄頁(yè)面
當(dāng)系統(tǒng)中物理內(nèi)存減少時(shí),Linux內(nèi)存管理子系統(tǒng)必須釋放物理頁(yè)面。這個(gè)任務(wù)由核心交換后臺(tái)進(jìn)程(kswapd )來(lái)完成。 

核心交換后臺(tái)進(jìn)程是一種特殊的核心線(xiàn)程。它是沒(méi)有虛擬內(nèi)存的進(jìn)程,在物理地址空間上以核心態(tài)運(yùn)行。核心交換后臺(tái)進(jìn)程的名字容易使人誤解,其實(shí)它完成的工作比僅僅將頁(yè)面交換到系統(tǒng)的交換文件中要多得多。其目標(biāo)是保證系統(tǒng)中有足夠的空閑頁(yè)面來(lái)維持內(nèi)存管理系統(tǒng)運(yùn)行效率。 

此進(jìn)程由核心的init進(jìn)程在系統(tǒng)啟動(dòng)時(shí)運(yùn)行,被核心交換定時(shí)器周期性的調(diào)用。 

當(dāng)定時(shí)器到時(shí)后,交換后臺(tái)進(jìn)程將檢查系統(tǒng)中的空閑頁(yè)面數(shù)是否太少。它使用兩個(gè)變量:free_pages_high 和free_page_low來(lái)判斷是否該釋放一些頁(yè)面。只要系統(tǒng)中的空閑頁(yè)面數(shù)大于free_pages_high,核心交換后臺(tái)進(jìn)程不做任何工作;它將睡眠到下一次定時(shí)器到時(shí)。在檢查中,核心交換后臺(tái)進(jìn)程將當(dāng)前被寫(xiě)到交換文件中的頁(yè)面數(shù)也計(jì)算在內(nèi),它使用nr_async_pages來(lái)記錄這個(gè)數(shù)值;當(dāng)有頁(yè)面被排入準(zhǔn)備寫(xiě)到交換文件隊(duì)列中時(shí),它將遞增一次,同時(shí)當(dāng)寫(xiě)入操作完成后遞減一次。如果系統(tǒng)中的空閑頁(yè)面數(shù)在free_pages_high甚至 free_pages_low以下時(shí),核心交換后臺(tái)進(jìn)程將通過(guò)三個(gè)途徑來(lái)減少系統(tǒng)中使用的物理頁(yè)面的個(gè)數(shù): 

減少緩沖與頁(yè)面cache的大小,
將系統(tǒng)V類(lèi)型的內(nèi)存頁(yè)面交換出去, 
換出或者丟棄頁(yè)面。 
如果系統(tǒng)中空閑頁(yè)面數(shù)低于free_pages_low,核心交換后臺(tái)進(jìn)程將在下次運(yùn)行之前釋放6個(gè)頁(yè)面。否則它只釋放3個(gè)。以上三種方法將依次使用直到系統(tǒng)釋放出足夠的空閑頁(yè)面。當(dāng)核心交換后臺(tái)進(jìn)程試圖釋放物理頁(yè)面時(shí)它將記錄使用的最后一種方法。下一次它會(huì)首先運(yùn)行上次最后成功的算法。 
  
當(dāng)釋放出足夠頁(yè)面后,核心交換后臺(tái)進(jìn)程將再次睡眠到下次定時(shí)器到時(shí)。如果導(dǎo)致核心交換后臺(tái)進(jìn)程釋放頁(yè)面的原因是系統(tǒng)中的空閑頁(yè)面數(shù)小于free_pages_low,則它只睡眠平時(shí)的一半時(shí)間。一旦空閑頁(yè)面數(shù)大于 free_pages_low則核心交換進(jìn)程的睡眠時(shí)間又會(huì)延長(zhǎng)。 

3.8.1  減少Page Cache和Buffer Cache的大小
Page Cache和Buffer cache中的頁(yè)面將被優(yōu)先考慮釋放到free_area數(shù)組中。Page Cache中包含的是內(nèi)存映射文件的頁(yè)面,其中有些可能是不必要的,它們浪費(fèi)了系統(tǒng)的內(nèi)存。而B(niǎo)uffer Cache中包含的是從物理設(shè)備中讀寫(xiě)的緩沖數(shù)據(jù),有些可能也是不必要的。當(dāng)系統(tǒng)中物理頁(yè)面開(kāi)始耗盡時(shí),從這些cache中丟棄頁(yè)面比較簡(jiǎn)單(它不需要象從內(nèi)存中交換一樣,無(wú)須對(duì)物理設(shè)備進(jìn)行寫(xiě)操作)。除了會(huì)使對(duì)物理設(shè)備及內(nèi)存映射文件的訪(fǎng)問(wèn)速度降低外,頁(yè)面丟棄策略沒(méi)有太多的副作用。如果策略得當(dāng),則所有進(jìn)程的損失相同。 

每次核心交換后臺(tái)進(jìn)程都會(huì)嘗試去壓縮這些cache。 

它首先檢查mem_map頁(yè)面數(shù)組中的頁(yè)面塊看是否有可以從物理內(nèi)存中丟棄出去的。當(dāng)系統(tǒng)中的空閑頁(yè)面數(shù)降低 到一個(gè)危險(xiǎn)水平時(shí),核心后臺(tái)交換進(jìn)程頻繁進(jìn)行交換,則檢查的頁(yè)面塊一般比較大。檢查的方式為輪轉(zhuǎn),每次試圖壓縮內(nèi)存映象時(shí),核心后臺(tái)交換進(jìn)程總是檢查不同的頁(yè)面塊。這是眾所周知的clock算法,每次在整個(gè)mem_map頁(yè)面數(shù)組中對(duì)頁(yè)面進(jìn)行檢查。 

核心后臺(tái)交換進(jìn)程將檢查每個(gè)頁(yè)面看是否已經(jīng)被page cache或者buffer cache緩沖。讀者可能已經(jīng)注意到共享頁(yè)面不在被考慮丟棄的頁(yè)面之列,這種頁(yè)面不會(huì)同時(shí)出現(xiàn)在這兩種cache中。如果頁(yè)面不在這兩者中任何一種之中時(shí),它將檢查mem_map頁(yè)面數(shù)組中的下一個(gè)頁(yè)面。 

緩存在buffer cache(或者頁(yè)面中的緩沖被緩存)中的頁(yè)面可以使緩沖分配和回收更加有效。內(nèi)存壓縮代碼將 力圖釋放在受檢頁(yè)面中包含的緩沖區(qū)。 

如果頁(yè)面中包含的所有緩沖區(qū)都被釋放,這個(gè)頁(yè)面也將被釋放。如果受檢頁(yè)面在Linux的page cache中,則它會(huì)從page cache中刪除并釋放。 

如果釋放出來(lái)了足夠的頁(yè)面,核心交換后臺(tái)進(jìn)程將等待到下一次被喚醒。這些被釋放的頁(yè)面都不是任何進(jìn)程虛擬內(nèi)存的一部分,這樣無(wú)須更新頁(yè)表。如果沒(méi)有足夠的緩沖頁(yè)面丟棄則交換進(jìn)程將試圖將一些共享頁(yè)面交換出去。 

3.8.2  換出系統(tǒng)V內(nèi)存頁(yè)面
系統(tǒng)V共享內(nèi)存是一種用來(lái)在進(jìn)程之間通過(guò)共享虛擬內(nèi)存來(lái)實(shí)現(xiàn)進(jìn)程通訊的機(jī)制。進(jìn)程是如何共享內(nèi)存將在IPC 一章中詳細(xì)討論。現(xiàn)在只需要說(shuō)明系統(tǒng)V共享內(nèi)存的任何區(qū)域都可以用一個(gè)shmid_ds數(shù)據(jù)結(jié)構(gòu)來(lái)表示就足夠了。 此結(jié)構(gòu)包含一個(gè)指向vm_area的鏈表指針,vm_area是為每個(gè)共享此虛擬內(nèi)存區(qū)域設(shè)計(jì)的結(jié)構(gòu)。它們之間通過(guò) vm_next_shared和vm_PRev_shared指針來(lái)連接。每個(gè)shmid_ds數(shù)據(jù)結(jié)構(gòu)包含一個(gè)頁(yè)表入口,每個(gè)入口描敘物理頁(yè)面與共享虛擬頁(yè)面之間的映射關(guān)系。 

核心交換后臺(tái)進(jìn)程同樣使用clock算法來(lái)將系統(tǒng)V共享內(nèi)存頁(yè)面交換出去。 

每次運(yùn)行時(shí),它要記得哪個(gè)共享虛擬內(nèi)存區(qū)域的哪個(gè)頁(yè)面是最后一個(gè)被交換出去的。兩個(gè)索引可以協(xié)助它完成這項(xiàng)工作,其一是一組shmid_ds數(shù)據(jù)結(jié)構(gòu)的索引,另一個(gè)是系統(tǒng)V共享內(nèi)存區(qū)域的頁(yè)表入口鏈表的索引。 這能夠保證對(duì)系統(tǒng)V共享內(nèi)存區(qū)域作出公平的選擇。 

由于對(duì)于給定的系統(tǒng)V共享虛擬內(nèi)存的物理頁(yè)面框號(hào)被保存在所有共享此虛擬內(nèi)存區(qū)域進(jìn)程的頁(yè)表中,核心 交換后臺(tái)進(jìn)程必須同時(shí)修改所有的頁(yè)表以表示頁(yè)面不再在內(nèi)存而在交換文件中。對(duì)于每個(gè)要交換出去的共享 頁(yè)面,核心交換后臺(tái)進(jìn)程可以在每個(gè)共享進(jìn)程的頁(yè)表中的頁(yè)表入口中找到它們(通過(guò)vm_area_struct數(shù)據(jù)結(jié) 構(gòu))。如果對(duì)應(yīng)此系統(tǒng)V共享內(nèi)存的頁(yè)面的進(jìn)程頁(yè)表入口是有效的,它可以將其轉(zhuǎn)變成無(wú)效,這樣換出頁(yè)表入口和共享頁(yè)面的用戶(hù)數(shù)將減一。換出系統(tǒng)V共享頁(yè)表入口的格式中包含一個(gè)對(duì)應(yīng)于一組shmid_ds數(shù)據(jù)結(jié)構(gòu)的索引以及一個(gè)對(duì)系統(tǒng)V共享內(nèi)存區(qū)域的頁(yè)表入口索引。 

如果所有共享進(jìn)程的頁(yè)表都被修改后此頁(yè)面的記數(shù)為0則共享頁(yè)面可以被寫(xiě)到交換文件中。同樣指向此系統(tǒng)V共享內(nèi)存區(qū)域的shmid_ds數(shù)據(jù)結(jié)構(gòu)鏈表中的頁(yè)表入口也被換出頁(yè)表入口代替。換出頁(yè)表入口雖然無(wú)效但是它包含一組打開(kāi)的交換文件的索引,同時(shí)還能找到換出頁(yè)面在文件中的偏移。當(dāng)頁(yè)面重新被帶入物理內(nèi)存時(shí),這些信息十分有用。 

3.8.3  換出和丟棄頁(yè)面
交換后臺(tái)進(jìn)程依次檢查系統(tǒng)中的每個(gè)進(jìn)程以確認(rèn)誰(shuí)最適合交換出去。 

比較好的候選者是那些可以被交換出去(有些是不可被交換出去的)并且只有一個(gè)或者幾個(gè)頁(yè)面在內(nèi)存中的進(jìn)程。只有那些包含的數(shù)據(jù)無(wú)法檢索的頁(yè)面才會(huì)從物理內(nèi)存中交換到系統(tǒng)交換文件中去。 

可執(zhí)行映象的許多內(nèi)容都可以從映象文件中讀出并且可以很容易重讀出來(lái)。例如,映象中的可執(zhí)行指令不能被映象本身修改,所以決不會(huì)寫(xiě)到交換文件中去。這些頁(yè)面直接丟棄就可以。當(dāng)進(jìn)程再次引用它們時(shí),只需要從可執(zhí)行映象文件中讀入內(nèi)存即可。 

一旦確定了將要被交換出去的進(jìn)程,交換后臺(tái)進(jìn)程將搜索其整個(gè)虛擬內(nèi)存區(qū)域以找到那些沒(méi)有共享或者加鎖的區(qū)域。 

Linux并不會(huì)將選中的進(jìn)程的整個(gè)可交換頁(yè)面都交換出去,它只刪除一小部分頁(yè)面。 

如果內(nèi)存被加鎖則頁(yè)面不能被交換或者丟棄。 

Linux交換算法使用頁(yè)面衰老算法。每個(gè)頁(yè)面有一個(gè)計(jì)數(shù)器來(lái)告訴核心交換后臺(tái)進(jìn)程這個(gè)頁(yè)面是否值得交換出 去(此計(jì)數(shù)器包含在mem_map_t結(jié)構(gòu)中)。當(dāng)頁(yè)面沒(méi)有使用或者沒(méi)有找到時(shí)將會(huì)衰老;交換后臺(tái)進(jìn)程僅僅交換 出那些老頁(yè)面。缺省操作是:當(dāng)頁(yè)面被首次分配時(shí),其年齡初始值為3,每次引用其年齡將加3,最大值為20。 每次核心交換后臺(tái)進(jìn)程運(yùn)行它來(lái)使頁(yè)面衰老-將年齡減1。這個(gè)缺省操作可以改變并且由于這個(gè)原因它們被存儲(chǔ)在swap_control數(shù)據(jù)結(jié)構(gòu)中。 

如果頁(yè)面變老了(age=0),則交換后臺(tái)進(jìn)程將進(jìn)一步來(lái)處理它。dirty頁(yè)面可以被交換出去。Linux在PTE中使 用一個(gè)硬件相關(guān)位來(lái)描敘頁(yè)面的這個(gè)特性(見(jiàn)圖3.2)。然而不是所有的dirty頁(yè)面都有必要寫(xiě)入到交換文件 中去。進(jìn)程的每個(gè)虛擬內(nèi)存區(qū)域可能有其自身的交換操作(由vm_area_struct結(jié)構(gòu)中的vm_ops指針表示),在 交換時(shí)使用的是這些方法。否則,交換后臺(tái)進(jìn)程將在交換文件中分配一個(gè)頁(yè)面并將頁(yè)面寫(xiě)到設(shè)備上去。 

頁(yè)面的頁(yè)表入口被標(biāo)志成無(wú)效但是它包含了頁(yè)面在在交換文件中位置的信息,包括一個(gè)表示頁(yè)面在交換文件中位置的偏移值以及使用的是哪個(gè)交換文件。但是不管使用的是哪種交換算法,以前那個(gè)物理頁(yè)面將被標(biāo)志成空閑并放入free_area中。Clean(或者not dirty)的頁(yè)面可以丟棄同時(shí)放入free_area以備重新使用。 

如果有足夠的可交換進(jìn)程頁(yè)面被交換出去或丟棄,則交換后臺(tái)進(jìn)程將再次睡眠。下次它醒來(lái)時(shí)將考慮系統(tǒng)中 的下一個(gè)進(jìn)程。通過(guò)這種方法,交換后臺(tái)進(jìn)程一點(diǎn)一點(diǎn)地將每個(gè)進(jìn)程的可交換或可丟棄物理頁(yè)面收回知道系 統(tǒng)再次處于平衡狀態(tài)。這比將整個(gè)進(jìn)程交換出去要公平得多。 

3.9  The Swap Cache
當(dāng)將頁(yè)面交換到交換文件中時(shí),Linux總是避免頁(yè)面寫(xiě),除非必須這樣做。當(dāng)頁(yè)面已經(jīng)被交換出內(nèi)存但是當(dāng)有進(jìn)程再次訪(fǎng)問(wèn)時(shí)又要將它重新調(diào)入內(nèi)存。只要頁(yè)面在內(nèi)存中沒(méi)有被寫(xiě)過(guò),則交換文件中的拷貝是有效的。 

Linux使用swap cache來(lái)跟蹤這些頁(yè)面。這個(gè)swap cache是一個(gè)頁(yè)表入口鏈表,每個(gè)對(duì)應(yīng)于系統(tǒng)中的物理頁(yè)面。這是一個(gè)對(duì)應(yīng)于交換出頁(yè)面的頁(yè)表入口并且描敘頁(yè)面放置在哪個(gè)交換文件中以及在交換文件中的位置。 如果swap cache入口為非0值,則表示在交換文件中的這一頁(yè)沒(méi)有被修改。如果此頁(yè)被修改(或者寫(xiě)入)。 則其入口從swap cache中刪除。 

當(dāng)Linux需要將一個(gè)物理頁(yè)面交換到交換文件時(shí),它將檢查swap cache,如果對(duì)應(yīng)此頁(yè)面存在有效入口,則 不必將這個(gè)頁(yè)面寫(xiě)到交換文件中。這是因?yàn)樽詮纳洗螐慕粨Q文件中將其讀出來(lái),內(nèi)存中的這個(gè)頁(yè)面還沒(méi)有被修改。 

swap cache中的入口是已換出頁(yè)面的頁(yè)表入口。它們雖被標(biāo)記為無(wú)效但是為L(zhǎng)inux提供了頁(yè)面在哪個(gè)交換文件中以及文件中的位置等信息。 


3.10  頁(yè)面的換入
保存在交換文件中的dirty頁(yè)面可能被再次使用到,例如,當(dāng)應(yīng)用程序向包含在已交換出物理頁(yè)面上的虛擬內(nèi)存區(qū)域?qū)懭霑r(shí)。對(duì)不在物理內(nèi)存中的虛擬內(nèi)存頁(yè)面的訪(fǎng)問(wèn)將引發(fā)頁(yè)面錯(cuò)誤。由于處理器不能將此虛擬地址轉(zhuǎn)換成物理地址,處理器將通知操作系統(tǒng)。由于已被交換出去,此時(shí)描敘此頁(yè)面的頁(yè)表入口被標(biāo)記成無(wú)效。處理器不能處理這種虛擬地址到物理地址的轉(zhuǎn)換,所以它將控制傳遞給操作系統(tǒng),同時(shí)通知操作系統(tǒng)頁(yè)面錯(cuò)誤的地址與原因。這些信息的格式以及處理器如何將控制傳遞給操作系統(tǒng)與具體硬件有關(guān)。 
處理器相關(guān)頁(yè)面錯(cuò)誤處理代碼將定位描敘包含出錯(cuò)虛擬地址對(duì)應(yīng)的虛擬內(nèi)存區(qū)域的vm_area_struct數(shù)據(jù)結(jié)構(gòu)。 它通過(guò)在此進(jìn)程的vm_area_struct中查找包含出錯(cuò)虛擬地址的位置直到找到為止。這些代碼與時(shí)間關(guān)系重大,進(jìn)程的vm_area_struct數(shù)據(jù)結(jié)構(gòu)特意安排成使查找操作時(shí)間更少。 

執(zhí)行完這些處理器相關(guān)操作并且找到出錯(cuò)虛擬地址的有效內(nèi)存區(qū)域后,頁(yè)面錯(cuò)處理過(guò)程其余部分和前面類(lèi)似。 

通用頁(yè)面錯(cuò)處理代碼為出錯(cuò)虛擬地址尋找頁(yè)表入口。如果找到的頁(yè)表入口是一個(gè)已換出頁(yè)面,Linux必須將其 交換進(jìn)入物理內(nèi)存。已換出頁(yè)面的頁(yè)表入口的格式與處理器類(lèi)型有關(guān),但是所有的處理器將這些頁(yè)面標(biāo)記成無(wú)效并把定位此頁(yè)面的必要信息放入頁(yè)表入口中。Linux利用這些信息以便將頁(yè)面交換進(jìn)物理入內(nèi)存。 

此時(shí)Linux知道出錯(cuò)虛擬內(nèi)存地址并且擁有一個(gè)包含頁(yè)面位置信息的頁(yè)表入口。vm_area_struct數(shù)據(jù)結(jié)構(gòu)可能包含將此虛擬內(nèi)存區(qū)域交換到物理內(nèi)存中的子程序:swapin。如果對(duì)此虛擬內(nèi)存區(qū)域存在swapin則Linux會(huì)使用它。這是已換出系統(tǒng)V共享內(nèi)存頁(yè)面的處理過(guò)程-因?yàn)橐褤Q出系統(tǒng)V共享頁(yè)面和普通的已換出頁(yè)面有少許不同。如果沒(méi)有swapin操作,這可能是Linux假定普通頁(yè)面無(wú)須特殊處理。 

系統(tǒng)將分配物理頁(yè)面并將已換出頁(yè)面讀入。關(guān)于頁(yè)面在交換文件中位置信息從頁(yè)表入口中取出。 

如果引起頁(yè)面錯(cuò)誤的訪(fǎng)問(wèn)不是寫(xiě)操作則頁(yè)面被保留在swap cache中并且它的頁(yè)表入口不再標(biāo)記為可寫(xiě)。如果 頁(yè)面隨后被寫(xiě)入,則將產(chǎn)生另一個(gè)頁(yè)面錯(cuò)誤,這時(shí)頁(yè)面被標(biāo)記為dirty,同時(shí)其入口從swap cache中刪除。 如果頁(yè)面沒(méi)有被寫(xiě)并且被要求重新?lián)Q出,Linux可以免除這次寫(xiě),因?yàn)轫?yè)面已經(jīng)存在于交換文件中。 

如果引起頁(yè)面從交換文件中讀出的操作是寫(xiě)操作,這個(gè)頁(yè)面將被從swap cache中刪除并且其頁(yè)表入口被標(biāo)記 成dirty且可寫(xiě)。
發(fā)表評(píng)論 共有條評(píng)論
用戶(hù)名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 洪江市| 济源市| 广德县| 仙游县| 南京市| 建水县| 和田市| 黄大仙区| 肇庆市| 皋兰县| 浑源县| 栾川县| 荆门市| 定州市| 右玉县| 新疆| 平乐县| 赫章县| 黔江区| 永康市| 西昌市| 福贡县| 韩城市| 宁南县| 昌图县| 绥江县| 许昌市| 泽州县| 萍乡市| 宜兰县| 即墨市| 额济纳旗| 秦安县| 依兰县| 十堰市| 丰县| 怀来县| 孟村| 分宜县| 墨脱县| 长乐市|