【文章內(nèi)容簡介】
。 /* 對于交換設備, swap_device屬性表示交換設備的主、次設備號 */ 52 spinlock_t sdev_lock。 /* 對于此設備的互斥鎖 */ 53 struct dentry * swap_file。 /* 對于交換文件, swap_file屬性指向該文件的 inode */ 54 struct vfsmount *swap_vfsmnt。 55 unsigned short * swap_map。/* 指向一張表,其每一字節(jié)按順序?qū)粨Q空間的一個頁面,字節(jié)的值代表了引用該頁面的進程數(shù) */ 56 unsigned int lowest_bit。 /*交換空間中的第一個沒有被任何進程使用的交換頁在 swap_map數(shù)組中的下標 */ 57 unsigned int highest_bit。 /* 交換空間中最后一個沒被任何進程使用的交換頁的下標 */ 58 unsigned int cluster_next。 /*上次從當前的 cluster中成功分配的交換頁面的后繼頁面在 swap_map數(shù)組中的下標 */ 59 unsigned int cluster_nr。 /* 當前 cluster中可供使用的交換頁面的個數(shù) */ 60 int prio。 /*交換空間的優(yōu)先級。優(yōu)先級越高,交換文件申請交換頁面的時候越優(yōu)先考慮 */ 61 int pages。 /* 表示該交換空間尚有多少空閑空間可供保存進程換出的物理頁 */ 62 unsigned long max。 63 int next。 /*指向下一項交換空間的的指針 */ 64 }。 153 struct swap_list_t { 154 int head。 /* head of priorityordered swapfile list */ 155 int next。 /* swapfile to be used next */ 156 }。 23 struct swap_list_t swap_list = {1, 1}。 注銷交換空間 int sys_swapoff(const char * swapfile)。 交換空間的工作 ? kswapd進程換出頁面時,調(diào)用 try_to_swap_out() 測試頁面的年齡。如果某物理頁面可以換出,則調(diào)用 get_swap_page向 換空間申請空閑頁面,得到一地址 entry。該地址寫入進程頁表中那個原來描述換出物理頁面的頁表項,替換了其中的頁幀地址。最后,調(diào)用rw_swap_page(),將換出的物理頁面寫到 entry指定的交換空間某個頁面中。 ? 反過來,當缺頁中斷發(fā)生時,缺頁中斷服務程序可以根據(jù)產(chǎn)生缺頁的地址(由 CR2寄存器給出),找到描述該頁面的頁表項。頁表項的 Present位應該為 0,最高 20位指出該頁面保存在哪個交換空間的哪個頁面中。然后,經(jīng)一系列函數(shù)調(diào)用后,讀入該頁面。 kswapd ? 當物理頁面不夠時,利用 kswapd釋放部分物理頁面,將它們的內(nèi)容寫到交換空間。 ? kswapd是一特殊的進程,稱內(nèi)核態(tài)線程( kernel thread)。 ? 注意, kernel thread完全不同于通常意義上的線程。它是沒有虛擬存儲空間的進程,它只運行在內(nèi)核態(tài),直接使用物理地址空間。同類型的進程還有 bdflush和 init。 ? kswapd的作用超越了字面上的描述。它不僅能將頁面換出到交換空間(交換區(qū)或交換文件),它也保證系統(tǒng)中有足夠的空閑頁面以保持存儲系統(tǒng)高效地運行。 請求調(diào)頁 ? 一旦一個可執(zhí)行鏡像映射到了一個進程的虛擬內(nèi)存中,它就可以開始執(zhí)行了。因為開始時只有鏡像開頭的一小部分裝入到了系統(tǒng)的物理內(nèi)存中,所以不久進程就會存取一些不在物理內(nèi)存中的虛擬內(nèi)存頁,這時處理器會通知 L i n u x發(fā)生了頁面錯誤。 ? 頁面錯誤將會描述頁面錯誤發(fā)生時的虛擬內(nèi)存地址和存取內(nèi)存操作的類型。 缺頁異常處理模式 缺頁異常處理 ? 當訪問一個無效的虛擬地址(可能是保護故障,也可能缺頁故障等)的時候,就會產(chǎn)生一個個頁故障 ? 統(tǒng)一的處理過程如下: ? 找到這個虛擬地址所在的 VMA; ? 如果必要,分配中間頁目錄表和頁表 ? 如果頁表項對應的物理頁面不存在,則調(diào)用這個 VMA的 nopage方法,它返回物理頁面的 page描述結(jié)構(gòu)(當然這只是其中的一種情況) ? 針對上面的情況,將物理頁面的地址填充到頁表中當頁故障處理完后,系統(tǒng)將重新啟動引起故障的指令,然后就可以正常訪問了 產(chǎn)生缺頁中斷 ? 當一個進程訪問了一個還沒有有效頁表項的虛擬地址時(即頁表項的 P位為 0),處理器將產(chǎn)生缺頁中斷,通知操作系統(tǒng),并將出現(xiàn)缺頁的虛存地址(在 CR2寄存器中)和缺頁時訪問虛存的模式一并傳遞給 LINUX的缺頁中斷服務程序。 缺頁中斷服務程序為 do_page_fault() set_trap_gate(14, amp。page_fault)。 /* arch/i386/kernel/ */ ENTRY(page_fault) /* arch/i386/kernel/ */ pushl $SYMBOL_NAME(do_page_fault) jmp error_code /* 異常中斷服務程序的統(tǒng)一入口 */ 中斷服務流程 ? 根據(jù)控制寄存器 CR2傳遞的缺頁地址,找到用來表示出現(xiàn)缺頁的虛擬存儲區(qū)的 vm_area_struct結(jié)構(gòu)。 ? 如果沒有找到與缺頁相對應的 vm_area_struct結(jié)構(gòu),那么說明進程訪問了一個非法存儲區(qū), LINUX向進程發(fā)送信號 SIGSEGV。 ? 接著檢測缺頁時訪問模式是否合法。如果進程對該頁的訪問超越權(quán)限,例如試圖對只允許讀操作的頁面進行寫操作,系統(tǒng)也將向該進程發(fā)送一個信號,通知進程的存儲訪問出錯。 ? 如果 Linux認為此頁面錯誤是合法的,它將處理此頁面錯誤。 ? Linux還必須區(qū)分頁面是在交換文件中還是作為文件鏡像的一部分存在于磁盤中。它靠檢查出錯頁面的頁面表來區(qū)分:如果頁面表的入口是無效的,但非空,說明頁面在交換文件中。 ? 最后, Linux調(diào)入所需的頁面并更新進程的頁面表。 頁面置換 ? 當一個進程需要把一個虛擬內(nèi)存頁面裝入到物理內(nèi)存而又沒有空閑的物理內(nèi)存時,操作系統(tǒng)必須將一個現(xiàn)在不用的頁面從物理內(nèi)存中扔掉以便為將要裝入的虛擬內(nèi)存頁騰出空間。 ? 選擇換出頁 ? 對換出頁的處理 頁面置換 ? 檢查是否存在可以從緩沖區(qū)中回收的塊 ? 若無,試圖回收共享內(nèi)存保留的頁框 ? 若無,用近似的 LRU(全局的最近最少使用)替代算法找到換出頁。 頁面換出 ? 如果將要扔掉的物理內(nèi)存頁一直沒有被改寫過,則操作系統(tǒng)將不保存此內(nèi)存頁,而只是簡單地將它扔掉。如果再需要此內(nèi)存頁時,再從文件鏡像中裝入。 ? 但是,如果此頁面已經(jīng)被修改過,操作系統(tǒng)就需要把頁面的內(nèi)容保存起來。這些頁面稱為“臟頁面”( dirty page)。當它們從內(nèi)存中移走時,將會被保存到一個特殊的交換文件中。 內(nèi)存管理中的高速緩存 ? Hardware Caches ? 硬件高速緩存:一個常用的硬件高速緩存是在處理器中,它一般保存著頁表的入口。 ? TLB ? Page Cache ? 頁面高速緩存 :它的作用是加快對磁盤中的文件的存取 ? Swap Cache ? 交換高速緩存:它只保存那些被修改過的頁面。 ? 只要在頁面被寫入到交換文件中后沒有被修改過,那么此頁面下一次從內(nèi)存中交換出來時就不用再寫入到交換文件中了,因為交換文件中已經(jīng)有了該頁面。這樣,該頁面就可以簡單地扔掉,節(jié)省了大量的系統(tǒng)操作。 ? Buffer Cache ? 緩沖區(qū)高速緩存:這個 buffer cache 中包含了被塊設備驅(qū)動使用的數(shù)據(jù)緩沖 頁面高速緩存 ? 對于已經(jīng)作好了磁盤映射的文件, L i n u x每次讀取一頁,并將讀取的頁面存儲到頁面高速緩存中。 ? 頁面高速緩存由 page_hash_table組成, page_hash_table 是一個包含指向 mem_map_t結(jié)構(gòu)指針的數(shù)組。 ? 每當從一個內(nèi)存映射文件中讀取一個頁面時,頁面都要從頁面高速緩存中讀取。如果頁面在高速緩存中,則將一個指向 mem_map_t的指針返回給頁面錯誤處理程序。否則,頁面必須從磁盤上讀入到內(nèi)存中。 ? 如果可能, L i n u x系統(tǒng)將會提前讀取文件中的下一個頁面,這樣,如果文件是順序執(zhí)行的,那么下一個頁面就已經(jīng)在內(nèi)存中了。隨著文件的讀入和執(zhí)行,頁面高速緩存也將變得越來越大。不用的頁面將被移出高速緩存。 緩沖區(qū)高速緩存 ? 緩沖區(qū)高速緩存中包含了用于塊設備驅(qū)動程序的數(shù)據(jù)緩沖區(qū)。這些緩沖區(qū)大小固定(例如 512 字節(jié)),包括從塊設備讀出的數(shù)據(jù)或者要寫到塊設備的數(shù)據(jù)。塊設備用設備標識符和要訪問的數(shù)據(jù)塊編號作為索引,用來快速定位數(shù)據(jù)塊。 ? 塊設備只能通過緩沖區(qū)高速緩存存取。如果數(shù)據(jù)可以在緩沖區(qū)高速緩存中找到,那就不需要從物理塊設備如硬盤上讀取,從而使訪問加快。 ? 參見 fs/ Slab分配器 ? 單單分配頁面的分配器肯定是不能滿足要求的。 ? 內(nèi)核中大量使用各種數(shù)據(jù)結(jié)構(gòu),大小從幾個字節(jié)到幾十上百 k不等,都取整到 2的冪次個頁面那是完全不現(xiàn)實的。 ? 需要新的內(nèi)存區(qū)域時,內(nèi)核從伙伴系統(tǒng)申請頁面,把它們劃分成一個個區(qū)域,取一個來滿足需求;如果某個頁面中的內(nèi)存區(qū)域都釋放了,頁面就交回到伙伴系統(tǒng)。 設計思想 ? 不同的數(shù)據(jù)類型用不同的方法分配內(nèi)存可能提高效率。比如需要初始化的數(shù)據(jù)結(jié)構(gòu),釋放后可以暫存著,再分配時就不必初始化了。 ? 內(nèi)核的函數(shù)常常重復地使用同一類型的內(nèi)存區(qū),緩存最近釋放的對象可以加速分配和釋放。 ? 對內(nèi)存的請求可以按照請求頻率來分類,頻繁使用的類型使用專門的緩存,很少使用的可以使用類似 2的冪次的通用緩存。 ? 使用 2的冪次大小的內(nèi)存區(qū)域時高速緩存沖突的概率較大,有可能通過仔細安排內(nèi)存區(qū)域的起始地址來減少高速緩存沖突。 ? 緩存一定數(shù)量的對象可以減少對 buddy系統(tǒng)的調(diào)用,從而節(jié)省時間并減少由此引起的高速緩存污染。 The slab allocator ponents ? The slab allocator groups objects into caches. ? Each cache is a store of objects of the same type. Relationship between cache and slab descriptors ? Full slabs, partially full slabs, and free slabs are linked in different lists Allocating a Slab to a Cache ? A newly created cache does not contain slab and therefore does not contain any free objects. ? New slabs are assigned to a cache only when both of the following are true: ? A request has been issued to allocate a new object. ? The cache does not include free object. Releasing a Slab from a Cache ? the slab allocator never releases the page frames of an empty slab on its own. ? In fact, a slab is released only if both of the following conditions are true: ? The buddy system is unable to satisfy a new request for a group of page frames (a zone is low on memory). ? The slab is empty—all the objects included in it are unused. Slab主要數(shù)據(jù)結(jié)構(gòu) ? struct kmem_cache_s ? struct kmem_slab_s ? Struct kmem_bufcrl_s k m e m s l a bn e x t s l a b i n c a c h ep r e v s l a b i n c a c h ek m e m