linux內(nèi)核解析之--內(nèi)存管理
掃描二維碼
隨時(shí)隨地手機(jī)看文章
本文主要介紹了linux內(nèi)核的內(nèi)存管理機(jī)制。什么是內(nèi)存管理機(jī)制??jī)?nèi)存管理主要負(fù)責(zé)完成當(dāng)進(jìn)程請(qǐng)求內(nèi)存時(shí)給進(jìn)程分配可用的內(nèi)存,當(dāng)進(jìn)程釋放內(nèi)存時(shí),回收相應(yīng)的內(nèi)存,同時(shí)負(fù)責(zé)跟蹤系統(tǒng)中相應(yīng)內(nèi)存的使用狀態(tài)。
Linux采用頁(yè)式內(nèi)存管理,頁(yè)是物理內(nèi)存管理的基本單位。但嚴(yán)格來(lái)說(shuō)Linux采用的是段頁(yè)式內(nèi)存管理,既分段也分頁(yè)。內(nèi)存映射的時(shí)候,先確定對(duì)應(yīng)的段,確定段基地址,段內(nèi)分頁(yè),再找到對(duì)應(yīng)的頁(yè)表項(xiàng),確定頁(yè)基地址,再由邏輯地址的低位確定的頁(yè)偏移量就能找到最終的物理地址。但Linux中的所有段地址都是0,即所有的段是相同的,之所以有段的概念是因?yàn)長(zhǎng)inux為了符合硬件體系。所以Linux實(shí)際采用的是頁(yè)式內(nèi)存管理,但段的概念在內(nèi)核中確實(shí)存在。
1、物理內(nèi)存的管理
Linux中首先將內(nèi)存分為若干個(gè)節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)下面又可分為1~3個(gè)區(qū),每個(gè)區(qū)下面會(huì)有若干個(gè)頁(yè)。
(1)節(jié)點(diǎn)
內(nèi)存節(jié)點(diǎn)主要是依據(jù)CPU訪問(wèn)代價(jià)不同而劃分的。一個(gè)CPU對(duì)應(yīng)一個(gè)節(jié)點(diǎn)。內(nèi)核數(shù)組node_data[]形式組織節(jié)點(diǎn),存儲(chǔ)的為struct page_data_t指針來(lái)描述內(nèi)存分區(qū)。
(2)區(qū)
內(nèi)核以struct_zone來(lái)描述內(nèi)存分區(qū)。內(nèi)核將所有的物理頁(yè)分為3個(gè)區(qū):ZONE_DMA、ZONE_NORMAL、ZONE_HIGHMEM。 ZONE_DMA區(qū)中包含的頁(yè)可以用來(lái)進(jìn)行DMA操作,即直接內(nèi)存訪問(wèn)操作,通常為物理內(nèi)存的起始16M。ZONE_NORMAL區(qū)包含的頁(yè)是可以進(jìn)行正常的內(nèi)存映射的頁(yè)物理內(nèi)存為16~896M。ZONE_HIGHMEM區(qū)稱(chēng)為“高端內(nèi)存”,該區(qū)所包含的頁(yè)不可以進(jìn)行永久映射,即不可以永久映射到內(nèi)核地址,物理內(nèi)存896M以后的。
高端內(nèi)存的邊界為896M的原因:32為L(zhǎng)inux系統(tǒng)中虛擬內(nèi)存空間為0-4G,3G-4G為內(nèi)核態(tài)。為了應(yīng)對(duì)內(nèi)核映射超過(guò)1G,Linux采取的策略:內(nèi)核地址空間的896M采用固定映射,映射方法:虛擬地址-3G=物理地址,只能映射896M,即3G~3G+896M,剩余的128M(3G+896M~4G)采用動(dòng)態(tài)映射。 Linux下以struct zone結(jié)構(gòu)體來(lái)表示一個(gè)區(qū),在該結(jié)構(gòu)體中變量struct page *zone_mem_map用來(lái)管理該區(qū)下的內(nèi)存映射表。
(3)頁(yè)
每一個(gè)物理頁(yè)框都使用一個(gè)數(shù)據(jù)結(jié)構(gòu)struct page來(lái)描述,該結(jié)構(gòu)體中的lru變量構(gòu)建用于LRU頁(yè)面置換的鏈表。在頁(yè)框空閑情況下,該成員變量用于構(gòu)建伙伴算法、鏈表同等大小的空閑內(nèi)存塊。大多數(shù)32bit的操作系統(tǒng)的頁(yè)大小為4KB。
2、伙伴算法
Linux采用的是伙伴(Buddy)算法對(duì)物理內(nèi)存進(jìn)行管理?;锇闄C(jī)制是操作系統(tǒng)的一種動(dòng)態(tài)存儲(chǔ)管理算法,該算法通過(guò)不斷平分較大的空閑內(nèi)存塊來(lái)獲得較小的空閑內(nèi)存塊,直到獲得所需的內(nèi)存塊。當(dāng)內(nèi)存釋放時(shí),該算法盡可能的合并空閑塊。該算法要求內(nèi)存塊的分配和合并都是以2的冪次方為單位。 在“區(qū)”內(nèi)存結(jié)構(gòu)體struct zone中有一struct free_area類(lèi)型的數(shù)組free_area[],數(shù)組最大為12個(gè)元素。數(shù)組的下標(biāo)k對(duì)應(yīng)著固定大小2^k個(gè)頁(yè)框空閑內(nèi)存區(qū)域的雙向鏈表頭。當(dāng)需要空閑塊為4(即2^2)個(gè)頁(yè)框時(shí)則查找free_area[2],如果沒(méi)有合適的,則查找free_area[3],直到找到合適的。
3、slab分配器
Linux中引入slab是為了減少對(duì)伙伴算法的調(diào)用,采用slab分配器來(lái)減少頻繁分配和釋放內(nèi)存數(shù)據(jù)結(jié)構(gòu)的開(kāi)銷(xiāo),同時(shí)減少了碎片的產(chǎn)生。slab分配機(jī)制是基于伙伴算法之上實(shí)現(xiàn)。 slab是基于一組對(duì)象緩存,把不同對(duì)象劃分為caches(物理內(nèi)存),每個(gè)cache保存一種類(lèi)型的對(duì)象,每個(gè)cache由一個(gè)或者多個(gè)slab組成,每個(gè)slab包含一個(gè)或者多個(gè)page組成。
每個(gè)slab處于3中狀態(tài)之一,即full、partial和empty(分別是滿、部分滿、空),其中滿狀態(tài)的slab沒(méi)有任何可分配的空閑對(duì)象。當(dāng)請(qǐng)求空閑對(duì)象時(shí)則從部分滿和空的slab中分配。
Linux內(nèi)核中的cache以結(jié)構(gòu)體kmem_cache_s來(lái)表示,結(jié)構(gòu)體中變量lists中存儲(chǔ)的為三個(gè)鏈表分別對(duì)應(yīng)于slab的三種狀態(tài)。 總結(jié)來(lái)說(shuō),當(dāng)為一對(duì)象申請(qǐng)內(nèi)存時(shí),首先查找到該對(duì)象的cache,然后查找cache中的slab列表,分配空閑內(nèi)存。當(dāng)釋放該對(duì)象內(nèi)存時(shí),則返回給該對(duì)象對(duì)應(yīng)的slab。這樣伙伴算法就不需要頻繁的進(jìn)行分配和合并操作。
4、虛擬內(nèi)存
(1)邏輯地址->線性地址->物理地址的轉(zhuǎn)換過(guò)程
邏輯地址即程序指令的地址,線性地址指頁(yè)式轉(zhuǎn)換前的地址(虛擬地址),物理地址則是物理內(nèi)存中的地址。
一個(gè)邏輯地址由兩部份組成,段標(biāo)識(shí)符: 段內(nèi)偏移量。段基址確定它所在的段居于整個(gè)存儲(chǔ)空間的位置,偏移量確定它在段內(nèi)的位置。Linux中由于段基址都是0,所以邏輯地址和線性地址相同。線性地址再通過(guò)MMU進(jìn)行轉(zhuǎn)換到物理地址,這個(gè)過(guò)程下面重點(diǎn)講下。 Linux也是內(nèi)存管理使用三級(jí)表結(jié)構(gòu):頁(yè)目錄、頁(yè)中間目錄、頁(yè)表。一個(gè)活動(dòng)任務(wù)都有一個(gè)頁(yè)目錄,大小一般為一頁(yè),頁(yè)目錄必須在內(nèi)存中。頁(yè)中間目錄可以跨越多個(gè)頁(yè)。頁(yè)表同樣可以跨越多個(gè)頁(yè),對(duì)應(yīng)具體的頁(yè)框。具體過(guò)程如下圖:
(2)頁(yè)面置換算法
Linux中頁(yè)結(jié)構(gòu)體的組織方式為雙向循環(huán)鏈表。Linux中頁(yè)面置換算法基于時(shí)鐘算法機(jī)制實(shí)現(xiàn),頁(yè)結(jié)構(gòu)體page中有一變量count專(zhuān)門(mén)用來(lái)計(jì)算頁(yè)面被引用的次數(shù)。每當(dāng)頁(yè)面被訪問(wèn)一次時(shí),count加1。在Linux后臺(tái),Linux周期性地掃描全局頁(yè)池,并且當(dāng)它在內(nèi)存中的所有頁(yè)間循環(huán)時(shí),將掃描的每一頁(yè)的count減1。age越大則使用頻率越高。最終內(nèi)核通過(guò)最近未使用(LRU)算法進(jìn)行頁(yè)面置換。
5、高速緩存
Linux使用了一系列的高速緩存相關(guān)的內(nèi)存管理技術(shù)來(lái)提高性能。此處的高速緩存并非
是物理緩存,而是軟件方法。Linux中主要包括以下幾個(gè)緩存:
(1)Buffer Cache,包括了用于塊設(shè)備驅(qū)動(dòng)程序的數(shù)據(jù)緩沖區(qū)。這些緩存區(qū)固定(一般512B),包括從塊設(shè)備要讀取的數(shù)據(jù)和要寫(xiě)入塊設(shè)備的數(shù)據(jù)。操作時(shí)先查看緩沖區(qū)。
(2)Page Cache,用來(lái)加快對(duì)磁盤(pán)上映像和數(shù)據(jù)的訪問(wèn)。用來(lái)緩存文件的邏輯內(nèi)容,一次緩存一頁(yè)。
(3)Swap Cache,只有改動(dòng)過(guò)的(或臟)頁(yè)才存在交換文件中,只要交換文件沒(méi)有再次修改,下次這些頁(yè)需要交換出時(shí)就不需要再寫(xiě)到交換文件中。
(4)Hardware Cache,常見(jiàn)方法是在處理器中PTE的高速緩存。這種情下處理器不需要直接讀取頁(yè)表,需要時(shí)把頁(yè)表放在緩存區(qū)中。CPU有轉(zhuǎn)換表緩沖區(qū)(TLB),來(lái)快速查找置換頁(yè)表。