?? linu家園-linux kernel核心中文手冊.htm
字號:
<P> </P>
<P></P>
<P>3.1.5 Access Control(訪問控制)</P>
<P> </P>
<P>頁表條目也包括訪問控制信息。當(dāng)處理器使用頁表條目將進(jìn)程的虛擬地址映射到物理地址的時候,它很容易利用訪問控制信息控制進(jìn)程不要用不允許的方式進(jìn)行訪問。</P>
<P> </P>
<P>有很多原因你希望限制對于內(nèi)存區(qū)域的訪問。一些內(nèi)存,比如包含執(zhí)行代碼,本質(zhì)上是只讀的代碼,操作系統(tǒng)應(yīng)該禁止進(jìn)程寫它的執(zhí)行代碼。反過來,包括數(shù)據(jù)的頁可以寫,但是如果試圖執(zhí)行這段內(nèi)存應(yīng)該失敗。大多數(shù)處理器有兩種執(zhí)行狀態(tài):核心態(tài)和用戶態(tài)。你不希望用戶直接執(zhí)行核心態(tài)的代碼或者存取核心數(shù)據(jù)結(jié)構(gòu),除非處理器運行在核心態(tài)。
</P>
<P>訪問控制信息放在PTE(page table entry)中,而且和具體處理器相關(guān)。圖3.2顯示了Alpha AXP的PTE。各個位意義如下:</P>
<P> </P>
<P>V 有效,這個PTE是否有效</P>
<P>FOE “Fault on Execute” 試圖執(zhí)行本頁代碼時,處理器是否要報告page fault,并將控制權(quán)傳遞給操作系統(tǒng)。</P>
<P>FOW “Fault on Write” 如上,在試圖寫本頁時產(chǎn)生page fault</P>
<P>FOR “fault on read” 如上,在試圖讀本頁時產(chǎn)生page fault</P>
<P>ASM 地址空間匹配。用于操作系統(tǒng)清除轉(zhuǎn)換緩沖區(qū)中的部分條目</P>
<P>KRE 核心態(tài)的代碼可以讀本頁</P>
<P>URE 用戶態(tài)的代碼可以讀本頁</P>
<P>GII 間隔因子,用于將一整塊映射到一個轉(zhuǎn)換緩沖條目而非多個。</P>
<P>KWE 核心態(tài)的代碼可以寫本頁</P>
<P>UWE 用戶態(tài)的代碼可以寫本頁</P>
<P>Page frame number
對于V位有效的PTE,包括了本PTE的物理頁編號;對于無效的PTE,如果不是0,包括了本頁是否在交換文件的信息。</P>
<P> </P>
<P>以下兩位由Linux定義并使用</P>
<P>_PAGE_DIRTY 如果設(shè)置,本頁需要寫到交換文件中。</P>
<P>_PAGE_ACCESSED Linux 使用,標(biāo)志一頁已經(jīng)訪問過</P>
<P> </P>
<P>3.2 Caches(高速緩存)</P>
<P>如果你用以上理論模型來實現(xiàn)一個系統(tǒng),它可以工作,但是不會太高效率。操作系統(tǒng)和處理器的設(shè)計師都盡力讓系統(tǒng)性能更高。除了使用更快的處理器、內(nèi)存等,最好的方法是維護(hù)有用信息和數(shù)據(jù)的高速緩存,這會使一些操作更快。Linux使用了一系列和高速緩存相關(guān)的內(nèi)存管理技術(shù):</P>
<P> </P>
<P>Buffer Cache: Buffer cache
包含了用于塊設(shè)備驅(qū)動程序的數(shù)據(jù)緩沖區(qū)。這些緩沖區(qū)大小固定(例如512字節(jié)),包括從塊設(shè)備讀出的數(shù)據(jù)或者要寫到塊設(shè)備的數(shù)據(jù)。塊設(shè)備是只能通過讀寫固定大小的數(shù)據(jù)塊來訪問的設(shè)備。所有的硬盤都是塊設(shè)備。塊設(shè)備用設(shè)備標(biāo)識符和要訪問的數(shù)據(jù)塊編號作為索引,用來快速定位數(shù)據(jù)塊。塊設(shè)備只能通過buffer
cache存取。如果數(shù)據(jù)可以在buffer cache中找到,那就不需要從物理塊設(shè)備如硬盤上讀取,從而使訪問加快。</P>
<P>參見fs/buffer.c</P>
<P>Page Cache
用來加快對磁盤上映像和數(shù)據(jù)的訪問。它用于緩存文件的邏輯內(nèi)容,一次一頁,并通過文件和文件內(nèi)的偏移來訪問。當(dāng)數(shù)據(jù)頁從磁盤讀到內(nèi)存中時,被緩存到page
cache中。</P>
<P>參見mm/filemap.c</P>
<P>Swap Cache
只有改動過的(或臟dirty)頁才存在交換文件中。只要它們寫到交換文件之后沒有再次修改,下一次這些頁需要交換出來的時候,就不需要再寫到交換文件中,因為該頁已經(jīng)在交換文件中了,直接廢棄該頁就可以了。在一個交換比較厲害的系統(tǒng),這會節(jié)省許多不必要和高代價的磁盤操作。</P>
<P>參見mm/swap_state.c mm/swapfile.c</P>
<P> </P>
<P> </P>
<P></P>
<P>Hardware
Cache:硬件高速緩存的常見的實現(xiàn)方法是在處理器里面:PTE的高速緩存。這種情況下,處理器不需要總是直接讀頁表,而在需要時把頁轉(zhuǎn)換表放在緩存區(qū)里。CPU里有轉(zhuǎn)換表緩沖區(qū)(TLB
Translation Look-aside Buffers),放置了系統(tǒng)中一個或多個進(jìn)程的頁表條目的緩存的拷貝。</P>
<P> </P>
<P>當(dāng)引用虛擬地址時,處理區(qū)試圖在TLB中尋找。如果找到了,它就直接將虛擬地址轉(zhuǎn)換到物理地址,進(jìn)而對數(shù)據(jù)執(zhí)行正確的操作。如果找不到,它就需要操作系統(tǒng)的幫助。它用信號通知操作系統(tǒng),發(fā)生了TLB
missing。一個和系統(tǒng)相關(guān)的機(jī)制將這個異常轉(zhuǎn)到操作系統(tǒng)相應(yīng)的代碼來處理。操作系統(tǒng)為這個地址映射生成新的TLB條目。當(dāng)異常清除之后,處理器再次嘗試轉(zhuǎn)換虛擬地址,這一次將會成功因為TLB中該地址有了一個有效的條目。</P>
<P> </P>
<P>高速緩存的副作用(不管是硬件或其他方式的)在于Linux必須花大量時間和空間來維護(hù)這些高速緩存區(qū),如果這些高速緩存區(qū)崩潰,系統(tǒng)也會崩潰。</P>
<P> </P>
<P>3.3 Linux Page Tables(Linux頁表)</P>
<P> </P>
<P>Linux假定了三級頁表。訪問的每一個頁表包括了下一級頁表的頁編號。圖3.3顯示了一個虛擬地址如何分為一系列字段:每一個字段提供了在一個頁表中的偏移量。為了將虛擬地址轉(zhuǎn)換為物理地址,處理器必須取得每一級字段的內(nèi)容,轉(zhuǎn)換為包括該頁表的物理頁內(nèi)的偏移,然后讀取下一級頁表的頁編號。重復(fù)三次直到包括虛擬地址的物理地址的頁編號找到為止。然后用虛擬地址中的最后一個字段:字節(jié)偏移量,在頁內(nèi)查找數(shù)據(jù)。</P>
<P> </P>
<P>Linux運行的每一個平臺都必須提供轉(zhuǎn)換宏,讓核心處理特定進(jìn)程的頁表。這樣,核心不需要知道頁表條目的具體結(jié)構(gòu)或者如何組織。通過這種方式,Linux成功地使用了相同的頁表處理程序用于Alpha和Intel
x86處理器,其中Alpha使用三級頁表,而Intel使用二級頁表。</P>
<P>參見include/asm/pgtable.h</P>
<P> </P>
<P>3.4 Page Allocation and Deallocation (頁的分配和回收)</P>
<P> </P>
<P>系統(tǒng)中對于物理頁有大量的需求。例如,當(dāng)程序映像加載到內(nèi)存中的時候,操作系統(tǒng)需要分配頁。當(dāng)程序結(jié)束執(zhí)行并卸載時需要釋放這些頁。另外為了存放核心相關(guān)的數(shù)據(jù)結(jié)構(gòu)比如頁表自身,也需要物理頁。這種用于分配和回收頁的機(jī)制和數(shù)據(jù)結(jié)構(gòu)對于維護(hù)虛擬內(nèi)存子系統(tǒng)的效率也許是最重要的。</P>
<P> </P>
<P>系統(tǒng)中的所有的物理頁都使用mem_map數(shù)據(jù)結(jié)構(gòu)來描述。這是一個mem_map_t結(jié)構(gòu)的鏈表,在啟動時進(jìn)行初始化。每一個mem_map_t(容易混淆的是這個結(jié)構(gòu)也被稱為page
結(jié)構(gòu))結(jié)構(gòu)描述系統(tǒng)中的一個物理頁。重要的字段(至少對于內(nèi)存管理而言)是:</P>
<P>參見include/linux/mm.h</P>
<P> </P>
<P>count 本頁用戶數(shù)目。如果本頁由多個進(jìn)程共享,計數(shù)器大于1。</P>
<P>Age 描述本頁的年齡。用于決定本頁是否可以廢棄或交換出去。</P>
<P>Map_nr mem_map_t描述的物理頁編號。 </P>
<P>頁分配代碼使用free_area向量來查找空閑的頁。整個緩沖管理方案用這種機(jī)制來支持。只要用了這種代碼,處理器使用的頁的大小和物理頁的機(jī)制就可以無關(guān)。</P>
<P> </P>
<P>每一個free_area單元包括頁塊的信息。數(shù)組中的第一個單元描述了單頁,下一個是2頁大小的塊,下一個是4頁大小的塊,以此類推,依次向上都是2的倍數(shù)。這個鏈表單元用作隊列的開頭,有指向mem_map數(shù)組中頁的數(shù)據(jù)結(jié)構(gòu)的指針。空閑的頁塊在這里排隊。Map是一個跟蹤這么大小的頁的分配組的位圖。如果頁塊中的第N塊空閑,則位圖中的第N位置位。</P>
<P> </P>
<P>圖3.4顯示了free_area結(jié)構(gòu)。單元0有一個空閑頁(頁編號0),單元2有2個4頁的空閑塊,第一個起始于頁編號4,第二個起始于頁編號56。</P>
<P> </P>
<P>3.4.1 Page Allocation (頁分配)</P>
<P>參見mm/page_alloc.c get_free_pages()</P>
<P> </P>
<P>Linux使用Buddy算法有效地分配和回收頁塊。頁分配代碼試圖分配一個由一個或多個物理頁組成的塊。頁分配使用2的冪數(shù)大小的塊。這意味著可以分配1頁大小,2頁大小,4頁大小的塊,依此類推。只要系統(tǒng)有滿足需要的足夠的空閑頁(nr_free_pages
>
min_free_pages),分配代碼就會在free_area中查找滿足需要大小的一個頁塊。Free_area中的每一個單元都有描述自身大小的頁塊的占用和空閑情況的位圖。例如,數(shù)組中的第2個單元擁有描述4頁大小的塊的空閑和占用的分配圖。</P>
<P> </P>
<P>這個算法首先找它請求大小的內(nèi)存頁塊。它跟蹤free_area數(shù)據(jù)結(jié)構(gòu)中的list單元隊列中的空閑頁的鏈表。如果請求大小的頁塊沒有空閑,就找下一個尺寸的塊(2倍于請求的大?。?。繼續(xù)這一過程一直到遍歷了所有的free_area或者找到了空閑頁塊。如果找到的頁塊大于請求的頁塊,則該塊將被分開成為合適大小的塊。因為所有的塊都是2的冪次的頁數(shù)組成,所以這個分割的過程比較簡單,你只需要將它平分就可以了??臻e的塊則放到適當(dāng)?shù)年犃?,而分配的頁塊則返回給調(diào)用者。</P>
<P> </P>
<P></P>
<P> </P>
<P>例如在圖3.4中,如果請求2頁的數(shù)據(jù)塊,第一個4頁塊(起始于頁編號4)將會被分為兩個2頁塊。起始于頁號4的第一個2頁塊將會被返回給調(diào)用者,而第二個2頁塊(起始于頁號6)將會排在free_area數(shù)組中的單元1中2頁空閑塊的隊列中。</P>
<P> </P>
<P>3.4.2 Page Deallocation(頁回收)</P>
<P> </P>
<P>分配頁塊的過程中將大的頁塊分為小的頁塊,將會使內(nèi)存更為零散。頁回收的代碼只要可能就把頁聯(lián)成大的頁塊。其實頁塊的大小很重要(2的冪數(shù)),因為這樣才能很容易將頁塊組成大的頁塊。</P>
<P> </P>
<P>只要一個頁塊回收,就檢查它的相鄰或一起的同樣大小的頁塊是否空閑。如果是這樣,就把它和新釋放的頁塊一起組成以一個新的下一個大小的空閑頁塊。每一次兩個內(nèi)存頁塊組合成為更大的頁塊時,頁回收代碼都要試圖將頁塊合并成為更大的塊。這樣,空閑的頁塊就會盡可能的大。</P>
<P>例如,在圖3.4,如果頁號1釋放,那么它會和已經(jīng)空閑的頁號0一起組合并放在free_area的單元1中空閑的2頁塊隊列中。</P>
<P> </P>
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -