頁(yè): 主要指一組數(shù)據(jù),可能保存在ram中也可能保存在磁盤里,主要強(qiáng)調(diào)一頁(yè)數(shù)據(jù);通常用固定大小(4K或4M)的頁(yè)來(lái)描述邏輯地址空間
頁(yè)框:限指RAM中的頁(yè),跟頁(yè)大小一致;通常用頁(yè)框來(lái)描述物理內(nèi)存空間;
頁(yè)表:頁(yè)表由頁(yè)表項(xiàng)組成,每一個(gè)頁(yè)表項(xiàng)指向一個(gè)頁(yè)框
X86提供了分段和分頁(yè)的內(nèi)存尋址方式,見(jiàn)下圖:
分段邏輯地址到線性地址轉(zhuǎn)換圖
二級(jí)頁(yè)表轉(zhuǎn)換圖
邏輯地址到物理地址轉(zhuǎn)換圖
具體參考http://blog.csdn.net/p0303230/archive/2007/12/24/1965094.aspx。
現(xiàn)在操作系統(tǒng)依靠特殊的硬件特性來(lái)禁止用戶程序直接與底層硬件部分進(jìn)行交互、或者禁止直接訪問(wèn)任意的物理地址。對(duì)于x86而言,這也是實(shí)模式和保護(hù)模式所完成的功能。
在x86架構(gòu)中,邏輯地址需要經(jīng)過(guò)分段、分頁(yè)過(guò)程轉(zhuǎn)為實(shí)際的物理地址,這是一個(gè)映射的過(guò)程,是由硬件規(guī)定的。但是,如何映射,映射規(guī)則是什么,則是軟件決定的,實(shí)際上這也是linux 內(nèi)核的一個(gè)重要功能。可以說(shuō)映射操作由cpu和MMU共同完成,但映射規(guī)則由內(nèi)核制定。
Linux 內(nèi)核內(nèi)存映射規(guī)則的實(shí)現(xiàn)主要通過(guò)建立合適的內(nèi)核頁(yè)表來(lái)完成。
內(nèi)核代碼運(yùn)行在0xc00000000-0xffffffff 的地址空間范圍,供1G。對(duì)內(nèi)核而言,顯然它應(yīng)該具備訪問(wèn)整個(gè)物理RAM的功能,這里即存在如下問(wèn)題:
內(nèi)核頁(yè)表的建立分為兩個(gè)階段,即臨時(shí)內(nèi)核頁(yè)表和最終內(nèi)核頁(yè)表:
臨時(shí)內(nèi)核頁(yè)表是在內(nèi)核編譯過(guò)程靜態(tài)初始化的,這就是說(shuō)內(nèi)核編譯時(shí)就先指定了映射規(guī)則,這樣保證內(nèi)核被加載后cpu就能正確找到內(nèi)核代碼并執(zhí)行。內(nèi)核引導(dǎo)加載程序?qū)?nèi)核代碼加載到0×100000(1MB)處(前1MB的部分物理內(nèi)存通常被BIOS使用,為避免把內(nèi)核裝入一組不連續(xù)的頁(yè)框里,linux干脆將內(nèi)核加載到1MB后)。通常編譯后內(nèi)核是小于7MB的,因此臨時(shí)內(nèi)核頁(yè)表的目的就是要保證能對(duì)這前8MB的物理RAM尋址,即將0×00000000~0x007fffff的物理內(nèi)存映射到線性地址0xc00000000~0xc07fffff的線性地址。為建立這前8MB的映射,需要兩個(gè)頁(yè)表、同時(shí)在頁(yè)目錄表中占用2項(xiàng)。
臨時(shí)頁(yè)全局目錄存放在swapper_pg_dir變量中,內(nèi)核把swapper_pg_dir所有項(xiàng)都填充為0,除0、1、0×300(十進(jìn)制768)和0×301(十進(jìn)制769)除外,后兩項(xiàng)包含了從0xc0000000到0xc07fffff間的所有線性地址。
在前面的討論中,一直存在一個(gè)為難題,即內(nèi)核本身的地址空間只有1G(0xc00000000-0xffffffff),對(duì)于RAM大于1G時(shí),內(nèi)核如何尋址? 因此顯然RAM等于1G是一個(gè)分界點(diǎn);另外,對(duì)32位的機(jī)器而言,通產(chǎn)其線性地址空間為0~4G范圍,當(dāng)RAM大于4G時(shí)如何尋址(主要針對(duì)cpu物理模型支持地址擴(kuò)展PAE時(shí))? 因此4G又是另一個(gè)分界點(diǎn)。
Linux的實(shí)現(xiàn)采用固定+動(dòng)態(tài)映射的方式來(lái)解決上述問(wèn)題。 具體做法是,內(nèi)核建立0~896MB的內(nèi)核映射,即將0xc0000000-0xf0000000映射到物理內(nèi)存前896MB;而對(duì)后128MB的線性地址空間(0xf000000~0xffffffff)采用動(dòng)態(tài)映射方法,即根據(jù)需要映射到內(nèi)存896MB以后的任意地址。具體實(shí)現(xiàn)機(jī)制請(qǐng)參考<<深入理解linux內(nèi)核>>。
上圖是linux內(nèi)核地址空間布局圖。內(nèi)核地址從PAGE_OFFSET(通常定義為3G,即通常說(shuō)的0-3G為用戶地址空間,3G-4G為內(nèi)核地址空間)開(kāi)始,接下來(lái)是內(nèi)核映像(.text等可執(zhí)行代碼區(qū));再接下來(lái)是mem_map數(shù)組,屬于內(nèi)核分配的動(dòng)態(tài)內(nèi)存區(qū),占用約為整個(gè)RAM的1%;接下來(lái)是隔離區(qū);接下來(lái)就是vmalloc區(qū)域;接著又是隔離區(qū);接著為從PKMAP_BASE到FIXADDR_START的由kmap()函數(shù)映射高端內(nèi)存區(qū);接著是從FIXADDR_START到FIXADDR_TOP是一個(gè)固定大小的線形地址空間,屬于專用頁(yè)面映射區(qū);最后128K為隔離區(qū)。最后說(shuō)一下隔離帶的作用,主要用于監(jiān)測(cè)內(nèi)存越界等。
本節(jié)主要介紹頁(yè)框管理和內(nèi)存區(qū)管理。頁(yè)框管理關(guān)注以頁(yè)為單位的管理;內(nèi)存區(qū)管理是運(yùn)行在頁(yè)框管理之上的RAM管理,但重點(diǎn)關(guān)注具有連續(xù)的物理地址和任意長(zhǎng)度的內(nèi)存但與序列的管理。這兩個(gè)管理重點(diǎn)需要解決的問(wèn)題分別是,頁(yè)框管理重點(diǎn)在于解決內(nèi)存外碎片問(wèn)題,而內(nèi)存區(qū)管理需要解決的是內(nèi)存內(nèi)碎片問(wèn)題。
頁(yè)框管理,顧名思義,就是以頁(yè)為單位,kernel管理內(nèi)存的一種機(jī)制。內(nèi)核必須能區(qū)分哪些頁(yè)框?qū)儆谶M(jìn)程,而哪些頁(yè)框?qū)儆趦?nèi)核代碼或內(nèi)核數(shù)據(jù);以及哪些頁(yè)框是空閑的等等。頁(yè)框的狀態(tài)信息保存在page數(shù)據(jù)結(jié)構(gòu)中,page結(jié)構(gòu)稱之為頁(yè)描述符。而所有的頁(yè)描述符存放在mem_map數(shù)組中,每個(gè)頁(yè)描述符長(zhǎng)度為32字節(jié),所以mem_map所需要的空間略小于整個(gè)RAM的1%.
受硬件歷史的約束,不同地址的內(nèi)存頁(yè)框曾經(jīng)被硬件用于一些特定目的,即不同地址內(nèi)的頁(yè)框可能在某個(gè)時(shí)期被某個(gè)硬件特別使用,因此,不能把所有的頁(yè)框當(dāng)做同一類型來(lái)處理。Linux內(nèi)核采用管理區(qū)的方式來(lái)實(shí)現(xiàn)頁(yè)框管理,即將不同類型的頁(yè)框劃歸到不同管理區(qū)。具體存在如下三類管理區(qū):
ZONE_DMA和ZONE_NORMAL管理區(qū)內(nèi)的頁(yè)框被直接映射到0xc0000000-0xf0000000的線性地址空間內(nèi),內(nèi)核可以直接訪問(wèn);而ZONE_HIGHMEM盡管也被映射到內(nèi)核地址空間內(nèi),但內(nèi)核不能直接訪問(wèn)。
在采用alloc_pages(gfp_mask)等請(qǐng)求分配頁(yè)框的函數(shù)中,gfp_mask 是一組標(biāo)志,包括__GFP_DMA,__GFP_HIGHMEM等,用于請(qǐng)求在某個(gè)特定管理區(qū)內(nèi)分配頁(yè)框。
內(nèi)核采用伙伴系統(tǒng)算法作為頁(yè)框的分配、釋放算法。 內(nèi)核應(yīng)該為分配一組連續(xù)的頁(yè)框而建立一種健壯、高效的分配策略?;锇樗惴苡行У目刂仆馑槠某霈F(xiàn)。其原理如下:
把所有的空閑頁(yè)面分為10個(gè)塊組,每組中塊的大小是2的冪次方個(gè)頁(yè)面,例如,第0組中塊的大小都為20(1個(gè)頁(yè)面),第1組中塊的大小為都為21(2個(gè)頁(yè)面),第9組中塊的大小都為29(512個(gè)頁(yè)面)。也就是說(shuō),每一組中塊的大小是相同的,且這同樣大小的塊形成一個(gè)鏈表??梢酝ㄟ^(guò)查看/proc/buddyinfo 來(lái)了解內(nèi)核伙伴算法的信息。
內(nèi)存區(qū)管理主要關(guān)注具有連續(xù)的物理地址和任意長(zhǎng)度的內(nèi)存但與序列的管理?;锇橄到y(tǒng)采用頁(yè)框作為管理單元,但如何處理小內(nèi)存區(qū)的請(qǐng)求呢?例如幾十或幾百個(gè)字節(jié)。如果為了分配一小片內(nèi)存而分配一個(gè)整頁(yè)框顯然是浪費(fèi)。對(duì)于內(nèi)存區(qū)的管理主要需要解決的是內(nèi)碎片問(wèn)題,內(nèi)碎片的產(chǎn)生主要由于請(qǐng)求的大小跟分配給它的大小不匹配導(dǎo)致的。
內(nèi)核采用slab分配器來(lái)解決上內(nèi)碎片問(wèn)題。
進(jìn)程地址空間是一個(gè)非負(fù)整數(shù)地址的有序集合:{0,1,2….}.程序員在編寫應(yīng)用程序,顯然需要關(guān)心它的程序所運(yùn)行的機(jī)器具有多大的RAM,哪些RAM又是空閑的。實(shí)質(zhì)上,對(duì)于x86 (32bit)機(jī)器來(lái)說(shuō),系統(tǒng)假設(shè)它獨(dú)立運(yùn)行在0-4G的地址空間里;即鏈接器將代碼按照0-3G(最后1G留給內(nèi)核)的地址來(lái)分配其邏輯地址,例如通常0×08048000用于.text段,這可以通過(guò)修改ld的鏈接腳本來(lái)設(shè)置新數(shù)值。 但這帶來(lái)了一個(gè)新的問(wèn)題,就是內(nèi)核如何管理進(jìn)程的地址空間呢? 顯然內(nèi)核需至少要知道如下信息:
這些都通過(guò)內(nèi)核的VMA的數(shù)據(jù)結(jié)構(gòu)表述某段地址空間,內(nèi)核稱之為線性區(qū)。線性區(qū)代表了一個(gè)范圍內(nèi)的連續(xù)的進(jìn)程地址空間的集合,這些地址空間邏輯上具有共同的特征,例如被映射到了同一個(gè)文件等。所有的線性區(qū)通過(guò)鏈表連接起來(lái),最終就構(gòu)成了進(jìn)程的地址空間。
每個(gè)進(jìn)程擁有的線性區(qū)數(shù)量有進(jìn)程本身決定的,可能很多,也可能很少。因此如何管理線性區(qū),如何高效的增加、刪除、查找線性區(qū)是內(nèi)核的重要內(nèi)容。當(dāng)前,內(nèi)核采用紅黑樹的結(jié)構(gòu)來(lái)管理線性區(qū)。對(duì)線性區(qū)的操作,如查到、增加、刪除等實(shí)質(zhì)也都牽涉到紅黑樹的操作,這些事線性區(qū)管理最核心的部分,關(guān)于紅黑樹的具體內(nèi)容請(qǐng)參考書籍《深入理解linux內(nèi)核》
線性區(qū)實(shí)現(xiàn)的另一個(gè)重要內(nèi)容是訪問(wèn)權(quán)限控制,例如可讀、可寫、可執(zhí)行的等等。
在linux中,對(duì)內(nèi)核申請(qǐng)內(nèi)存和應(yīng)用程序申請(qǐng)內(nèi)存采用不同的策略:
而對(duì)用戶程序而言,情況完全不同:
上述策略的核心不同就在于內(nèi)存分配的時(shí)機(jī),對(duì)內(nèi)核而言是立即分配;而對(duì)用戶進(jìn)程而言,是推遲分配,準(zhǔn)確說(shuō)是按需分配,即真正需要時(shí)才分配。 實(shí)現(xiàn)按需分配的核心就是缺頁(yè)異常處理。
當(dāng)cpu訪問(wèn)的頁(yè)不在RAM主存中時(shí),就會(huì)導(dǎo)致缺頁(yè)異常中斷。在linue 內(nèi)核中,對(duì)缺頁(yè)中斷處理的大致邏輯是:
這個(gè)過(guò)程牽涉到兩個(gè)重要的技術(shù),請(qǐng)求調(diào)頁(yè)和寫時(shí)復(fù)制。
在linux中,隨著系統(tǒng)的運(yùn)行,頁(yè)框逐漸被分配,為防止內(nèi)存被耗盡,系統(tǒng)需要“定期”運(yùn)行頁(yè)框回收算法。其主要原理就是將一些暫時(shí)不用的頁(yè)框,例如未運(yùn)行的進(jìn)程占用的頁(yè)框,交換到swap空間中,以回收頁(yè)框;下一次使用這些頁(yè)框時(shí)再?gòu)膕wap空間將數(shù)據(jù)拷入新的頁(yè)框中。這也是為什么你的應(yīng)用程序所實(shí)際需要的內(nèi)存可以超過(guò)整個(gè)RAM的原因,因?yàn)槟愕拇疟P也可以當(dāng)做“RAM”來(lái)使用,存放臨時(shí)數(shù)據(jù)。
Linux的頁(yè)框回收算法(PFRA)不回收下列類型的頁(yè)框:
這些頁(yè)框不會(huì)被回收,也就是說(shuō)這些頁(yè)不會(huì)被交換出去,即他們是常駐RAM中,系統(tǒng)啟動(dòng)一旦分配后就常駐內(nèi)存,這也是我們常說(shuō)的系統(tǒng)占用內(nèi)存。
能被回收的頁(yè)框主要是用戶態(tài)進(jìn)程申請(qǐng)的頁(yè)框和內(nèi)核高速緩存。
對(duì)于一個(gè)進(jìn)程而言,用戶程序跟內(nèi)核協(xié)作共同完成某項(xiàng)功能。只不過(guò)內(nèi)核是系統(tǒng)啟動(dòng)加載后常駐內(nèi)存,用戶程序通過(guò)系統(tǒng)調(diào)用(int 0×80)來(lái)執(zhí)行內(nèi)核代碼;而且內(nèi)核代碼是被多個(gè)進(jìn)程共享使用的,這也是處處要求內(nèi)核代碼必須是可重入的原因。
Linux的分段分頁(yè)機(jī)制,http://blog.csdn.net/p0303230/archive/2007/12/24/1965094.aspx
內(nèi)存觀點(diǎn),http://www.linuxforum.net/forum/showflat.php?Cat=&Board=linuxK&Number=290225&page=&view=&sb=&o=&vc=1
《深入理解linux內(nèi)核》
《linux設(shè)備驅(qū)動(dòng)程序》
《深入理解計(jì)算機(jī)系統(tǒng)》
《深入了解Linux虛擬內(nèi)存管理》
這篇文章也算紀(jì)念我的大學(xué)生活吧,曾記得大三那年我整日逃課,躲在圖書館看《linux內(nèi)核源代碼情景分析》,看懂了多少如今也早已忘了,只記得我整整幾個(gè)月基本都在圖書館度過(guò);那時(shí)的我總以為內(nèi)核是最高深的,所以要努力學(xué)習(xí)。 大四的畢業(yè)設(shè)計(jì)也是關(guān)于嵌入式設(shè)備驅(qū)動(dòng)的,而且還在一家公司實(shí)習(xí)做了一個(gè)實(shí)際應(yīng)用的設(shè)備驅(qū)動(dòng),前后性能提高很大,為此還被公司老總特別表?yè)P(yáng)過(guò),在那對(duì)一個(gè)實(shí)習(xí)生而言已屬相當(dāng)難得了。 后來(lái)的生活卻是世事難料,研究生經(jīng)歷了很多故事;畢業(yè)后也沒(méi)有找到內(nèi)核相關(guān)的工作,很多公司給出的理由是內(nèi)核開(kāi)發(fā)需要有經(jīng)驗(yàn)的人,尤其是我當(dāng)時(shí)孤身一人從南方一所學(xué)校來(lái)到北京,而我們學(xué)校在這里沒(méi)有了競(jìng)爭(zhēng)力,盡管在南方還算受歡迎。
如今的我對(duì)內(nèi)核扔懷抱敬畏之心,但不再膜拜了。工程更多的是改變我們當(dāng)下的生活,而真正的改變未來(lái)、改變世界的是研究,是認(rèn)識(shí)哪些枯燥數(shù)字后面的規(guī)律。
聯(lián)系客服