Linux內(nèi)存管理以程序員視角_第1頁(yè)
Linux內(nèi)存管理以程序員視角_第2頁(yè)
Linux內(nèi)存管理以程序員視角_第3頁(yè)
Linux內(nèi)存管理以程序員視角_第4頁(yè)
Linux內(nèi)存管理以程序員視角_第5頁(yè)
已閱讀5頁(yè),還剩14頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、.Linux內(nèi)存管理 以程序員視角Linux內(nèi)存管理摘要:本章首先以應(yīng)用程序開(kāi)發(fā)者的角度審視Linux的進(jìn)程內(nèi)存管理,在此根底上逐步深化到內(nèi)核中討論系統(tǒng)物理內(nèi)存管理和內(nèi)核內(nèi)存的使用方法。力求從外到內(nèi)、水到渠成地引導(dǎo)網(wǎng)友分析Linux的內(nèi)存管理與使用。在本章最后,我們給出一個(gè)內(nèi)存映射的實(shí)例,幫助網(wǎng)友們理解內(nèi)核內(nèi)存管理與用戶內(nèi)存管理之間的關(guān)系,希望大家最終能駕馭Linux內(nèi)存管理。前言內(nèi)存管理一向是所有操作系統(tǒng)書(shū)籍不惜筆墨重點(diǎn)討論的內(nèi)容,無(wú)論市面上或是網(wǎng)上都充滿著大量涉及內(nèi)存管理的教材和資料。因此,我們這里所要寫(xiě)的Linux內(nèi)存管理采取避重就輕的策略,從理論層面就不去班門(mén)弄斧,貽笑大方了。我們最想

2、做的和可能做到的是從開(kāi)發(fā)者的角度談?wù)剬?duì)內(nèi)存管理的理解,最終目的是把我們?cè)趦?nèi)核開(kāi)發(fā)中使用內(nèi)存的經(jīng)歷和對(duì)Linux內(nèi)存管理的認(rèn)識(shí)與大家共享。當(dāng)然,這其中我們也會(huì)涉及到一些諸如段頁(yè)等內(nèi)存管理的根本理論,但我們的目的不是為了強(qiáng)調(diào)理論,而是為了指導(dǎo)理解開(kāi)發(fā)中的理論,所以僅僅點(diǎn)到為止,不做深究。遵循"理論來(lái)源于理論"的"教條",我們先不必一下子就鉆入內(nèi)核里去看系統(tǒng)內(nèi)存到底是如何管理,那樣往往會(huì)讓你陷入似懂非懂的窘境我當(dāng)年就犯了這個(gè)錯(cuò)誤!。所以最好的方式是先從外部用戶編程范疇來(lái)觀察進(jìn)程如何使用內(nèi)存,等到大家對(duì)內(nèi)存的使用有了較直觀的認(rèn)識(shí)后,再深化到內(nèi)核中去學(xué)習(xí)內(nèi)存如何被

3、管理等理論知識(shí)。最后再通過(guò)一個(gè)實(shí)例編程將所講內(nèi)容融會(huì)貫穿。進(jìn)程與內(nèi)存進(jìn)程如何使用內(nèi)存?毫無(wú)疑問(wèn),所有進(jìn)程執(zhí)行的程序都必須占用一定數(shù)量的內(nèi)存,它或是用來(lái)存放從磁盤(pán)載入的程序代碼,或是存放取自用戶輸入的數(shù)據(jù)等等。不過(guò)進(jìn)程對(duì)這些內(nèi)存的管理方式因內(nèi)存用處不一而不盡一樣,有些內(nèi)存是事先靜態(tài)分配和統(tǒng)一回收的,而有些卻是按需要?jiǎng)討B(tài)分配和回收的。對(duì)任何一個(gè)普通進(jìn)程來(lái)講,它都會(huì)涉及到5種不同的數(shù)據(jù)段。稍有編程知識(shí)的朋友都能想到這幾個(gè)數(shù)據(jù)段中包含有"程序代碼段"、"程序數(shù)據(jù)段"、"程序堆棧段"等。不錯(cuò),這幾種數(shù)據(jù)段都在其中,但除了以上幾種數(shù)據(jù)段之外,進(jìn)程

4、還另外包含兩種數(shù)據(jù)段。下面我們來(lái)簡(jiǎn)單歸納一下進(jìn)程對(duì)應(yīng)的內(nèi)存空間中所包含的5種不同的數(shù)據(jù)區(qū)。代碼段:代碼段是用來(lái)存放可執(zhí)行文件的操作指令,也就是說(shuō)是它是可執(zhí)行程序在內(nèi)存中的鏡像。代碼段需要防止在運(yùn)行時(shí)被非法修改,所以只準(zhǔn)許讀取操作,而不允許寫(xiě)入修改操作-它是不可寫(xiě)的。數(shù)據(jù)段:數(shù)據(jù)段用來(lái)存放可執(zhí)行文件中已初始化全局變量,換句話說(shuō)就是存放程序靜態(tài)分配的變量和全局變量。BSSBSS段包含了程序中未初始化的全局變量,在內(nèi)存中bss段全部置零。堆heap:堆是用于存放進(jìn)程運(yùn)行中被動(dòng)態(tài)分配的內(nèi)存段,它的大小并不固定,可動(dòng)態(tài)擴(kuò)張或縮減。當(dāng)進(jìn)程調(diào)用malloc等函數(shù)分配內(nèi)存時(shí),新分配的內(nèi)存就被動(dòng)態(tài)添加到堆上堆被

5、擴(kuò)張;當(dāng)利用free等函數(shù)釋放內(nèi)存時(shí),被釋放的內(nèi)存從堆中被剔除堆被縮減棧:棧是用戶存放程序臨時(shí)創(chuàng)立的部分變量,也就是說(shuō)我們函數(shù)括弧""中定義的變量但不包括static聲明的變量,static意味著在數(shù)據(jù)段中存放變量。除此以外,在函數(shù)被調(diào)用時(shí),其參數(shù)也會(huì)被壓入發(fā)起調(diào)用的進(jìn)程棧中,并且待到調(diào)用完畢后,函數(shù)的返回值也會(huì)被存放回棧中。由于棧的先進(jìn)先出特點(diǎn),所以棧特別方便用來(lái)保存/恢復(fù)調(diào)用現(xiàn)場(chǎng)。從這個(gè)意義上講,我們可以把堆??闯梢粋€(gè)存放、交換臨時(shí)數(shù)據(jù)的內(nèi)存區(qū)。進(jìn)程如何組織這些區(qū)域?上述幾種內(nèi)存區(qū)域中數(shù)據(jù)段、BSS和堆通常是被連續(xù)存儲(chǔ)的-內(nèi)存位置上是連續(xù)的,而代碼段和棧往往會(huì)被獨(dú)立存放

6、。有趣的是,堆和棧兩個(gè)區(qū)域關(guān)系很"曖昧",他們一個(gè)向下"長(zhǎng)"i386體系構(gòu)造中棧向下、堆向上,一個(gè)向上"長(zhǎng)",相對(duì)而生。但你不必?fù)?dān)憂他們會(huì)碰頭,因?yàn)樗麄冎g間隔很大到底大到多少,你可以從下面的例子程序計(jì)算一下,絕少有時(shí)機(jī)能碰到一起。以下圖簡(jiǎn)要描繪了進(jìn)程內(nèi)存區(qū)域的分布:"事實(shí)勝于雄辯",我們用一個(gè)小例子原形取自?User-Level Memory Management?來(lái)展示上面所講的各種內(nèi)存區(qū)的差異與位置。includestdio.h includemalloc.h includeunistd.h int bss_

7、var;int data_var0=1;int mainint argc,char*argvprintf"below are addresses of types of process's memn";printf"Text location:n";printf"tAddress of mainCode Segment:%pn",main;printf"_n";int stack_var0=2;printf"Stack Location:n";printf"tInitial e

8、nd of stack:%pn",&stack_var0;int stack_var1=3;printf"tnew end of stack:%pn",&stack_var1;printf"_n";printf"Data Location:n";printf"tAddress of data_varData Segment:%pn",&data_var0;static int data_var1=4;printf"tNew end of data_varData Segm

9、ent:%pn",&data_var1;printf"_n";printf"BSS Location:n";printf"tAddress of bss_var:%pn",&bss_var;printf"_n";char*b=sbrkptrdiff_t0;printf"Heap Location:n";printf"tInitial end of heap:%pn",b;brkb+4;b=sbrkptrdiff_t0;printf"tNew

10、 end of heap:%pn",b;return 0;它的結(jié)果如下below are addresses of types of process's mem Text location:Address of mainCode Segment:0x 8048388 _Stack Location:Initial end of stack:0xbffffab4 new end of stack:0xbffffab0 _Data Location:Address of data_varData Segment:0x 8049758 New end of data_varData

11、 Segment:0x 804975c _BSS Location:Address of bss_var:0x 8049864 _Heap Location:Initial end of heap:0x 8049868 New end of heap:0x 804986c利用size命令也可以看到程序的各段大小,比方執(zhí)行size example會(huì)得到text data bss dec hex filename 1654 2808 1942 796 example但這些數(shù)據(jù)是程序編譯的靜態(tài)統(tǒng)計(jì),而上面顯示的是進(jìn)程運(yùn)行時(shí)的動(dòng)態(tài)值,但兩者是對(duì)應(yīng)的。通過(guò)前面的例子,我們對(duì)進(jìn)程使用的邏輯內(nèi)存分布已先睹為

12、快。這部分我們就繼續(xù)進(jìn)入操作系統(tǒng)內(nèi)核看看,進(jìn)程對(duì)內(nèi)存詳細(xì)是如何進(jìn)展分配和管理的。從用戶向內(nèi)核看,所使用的內(nèi)存表象形式會(huì)依次經(jīng)歷"邏輯地址"-"線性地址"-"物理地址"幾種形式關(guān)于幾種地址的解釋在前面已經(jīng)講述了。邏輯地址經(jīng)段機(jī)制轉(zhuǎn)化成線性地址;線性地址又經(jīng)過(guò)頁(yè)機(jī)制轉(zhuǎn)化為物理地址。但是我們要知道Linux系統(tǒng)雖然保存了段機(jī)制,但是將所有程序的段地址都定死為0-4G,所以雖然邏輯地址和線性地址是兩種不同的地址空間,但在Linux中邏輯地址就等于線性地址,它們的值是一樣的。沿著這條線索,我們所研究的主要問(wèn)題也就集中在下面幾個(gè)問(wèn)題。1.進(jìn)程空間

13、地址如何管理?2.進(jìn)程地址如何映射到物理內(nèi)存?3.物理內(nèi)存如何被管理?以及由上述問(wèn)題引發(fā)的一些子問(wèn)題。如系統(tǒng)虛擬地址分布;內(nèi)存分配接口;連續(xù)內(nèi)存分配與非連續(xù)內(nèi)存分配等。進(jìn)程內(nèi)存空間Linux操作系統(tǒng)采用虛擬內(nèi)存管理技術(shù),使得每個(gè)進(jìn)程都有各自互不干預(yù)的進(jìn)程地址空間。該空間是塊大小為4G的線性虛擬空間,用戶所看到和接觸到的都是該虛擬地址,無(wú)法看到實(shí)際的物理內(nèi)存地址。利用這種虛擬地址不但能起到保護(hù)操作系統(tǒng)的效果用戶不能直接訪問(wèn)物理內(nèi)存,而且更重要的是,用戶程序可使用比實(shí)際物理內(nèi)存更大的地址空間詳細(xì)的原因請(qǐng)看硬件根底部分。在討論進(jìn)程空間細(xì)節(jié)前,這里先要澄清下面幾個(gè)問(wèn)題:l第一、4G的進(jìn)程地址空間被人為

14、的分為兩個(gè)部分-用戶空間與內(nèi)核空間。用戶空間從0到3G0xC 0000000,內(nèi)核空間占據(jù)3G到4G。用戶進(jìn)程通常情況下只能訪問(wèn)用戶空間的虛擬地址,不能訪問(wèn)內(nèi)核空間虛擬地址。只有用戶進(jìn)程進(jìn)展系統(tǒng)調(diào)用代表用戶進(jìn)程在內(nèi)核態(tài)執(zhí)行等時(shí)刻可以訪問(wèn)到內(nèi)核空間。l第二、用戶空間對(duì)應(yīng)進(jìn)程,所以每當(dāng)進(jìn)程切換,用戶空間就會(huì)跟著變化;而內(nèi)核空間是由內(nèi)核負(fù)責(zé)映射,它并不會(huì)跟著進(jìn)程改變,是固定的。內(nèi)核空間地址有自己對(duì)應(yīng)的頁(yè)表init_mm.pgd,用戶進(jìn)程各自有不同的頁(yè)表。l第三、每個(gè)進(jìn)程的用戶空間都是完全獨(dú)立、互不相干的。不信的話,你可以把上面的程序同時(shí)運(yùn)行10次當(dāng)然為了同時(shí)運(yùn)行,讓它們?cè)诜祷厍耙煌?00秒吧,你

15、會(huì)看到10個(gè)進(jìn)程占用的線性地址一模一樣。進(jìn)程內(nèi)存管理進(jìn)程內(nèi)存管理的對(duì)象是進(jìn)程線性地址空間上的內(nèi)存鏡像,這些內(nèi)存鏡像其實(shí)就是進(jìn)程使用的虛擬內(nèi)存區(qū)域memory region。進(jìn)程虛擬空間是個(gè)32或64位的"平坦"獨(dú)立的連續(xù)區(qū)間地址空間空間的詳細(xì)大小取決于體系構(gòu)造。要統(tǒng)一管理這么大的平坦空間可絕非易事,為了方便管理,虛擬空間被劃分為許多大小可變的但必須是4096的倍數(shù)內(nèi)存區(qū)域,這些區(qū)域在進(jìn)程線性地址中像停車(chē)位一樣有序排列。這些區(qū)域的劃分原那么是"將訪問(wèn)屬性一致的地址空間存放在一起",所謂訪問(wèn)屬性在這里無(wú)非指的是"可讀、可寫(xiě)、可執(zhí)行等"。假

16、如你要查看某個(gè)進(jìn)程占用的內(nèi)存區(qū)域,可以使用命令cat/proc/pid/maps獲得pid是進(jìn)程號(hào),你可以運(yùn)行上面我們給出的例子-./example&;pid便會(huì)打印到屏幕,你可以發(fā)現(xiàn)很多類似于下面的數(shù)字信息。由于程序example使用了動(dòng)態(tài)庫(kù),所以除了example本身使用的的內(nèi)存區(qū)域外,還會(huì)包含那些動(dòng)態(tài)庫(kù)使用的內(nèi)存區(qū)域區(qū)域順序是:代碼段、數(shù)據(jù)段、bss段。我們下面只抽出和example有關(guān)的信息,除了前兩行代表的代碼段和數(shù)據(jù)段外,最后一行是進(jìn)程使用的??臻g。-08048000-08049000 r-xp 0000000003:03 439029/home/mm/src/exampl

17、e 08049000-0804a000 rw-p 0000000003:03 439029/home/mm/src/examplebfffe000-c 0000000 rwxp ffff00000:00 0-每行數(shù)據(jù)格式如下:內(nèi)存區(qū)域開(kāi)場(chǎng)-完畢訪問(wèn)權(quán)限偏移主設(shè)備號(hào):次設(shè)備號(hào)i節(jié)點(diǎn)文件。注意,你一定會(huì)發(fā)現(xiàn)進(jìn)程空間只包含三個(gè)內(nèi)存區(qū)域,似乎沒(méi)有上面所提到的堆、bss等,其實(shí)并非如此,程序內(nèi)存段和進(jìn)程地址空間中的內(nèi)存區(qū)域是種模糊對(duì)應(yīng),也就是說(shuō),堆、bss、數(shù)據(jù)段初始化過(guò)的都在進(jìn)程空間中由數(shù)據(jù)段內(nèi)存區(qū)域表示。在Linux內(nèi)核中對(duì)應(yīng)進(jìn)程內(nèi)存區(qū)域的數(shù)據(jù)構(gòu)造是vm_area_struct,內(nèi)核將每個(gè)內(nèi)存區(qū)域作

18、為一個(gè)單獨(dú)的內(nèi)存對(duì)象管理,相應(yīng)的操作也都一致。采用面向?qū)ο蠓椒ㄊ筕MA構(gòu)造體可以代表多種類型的內(nèi)存區(qū)域-比方內(nèi)存映射文件或進(jìn)程的用戶空間棧等,對(duì)這些區(qū)域的操作也都不盡一樣。vm_area_strcut構(gòu)造比較復(fù)雜,關(guān)于它的詳細(xì)構(gòu)造請(qǐng)參閱相關(guān)資料。我們這里只對(duì)它的組織方法做一點(diǎn)補(bǔ)充說(shuō)明。vm_area_struct是描繪進(jìn)程地址空間的根本管理單元,對(duì)于一個(gè)進(jìn)程來(lái)說(shuō)往往需要多個(gè)內(nèi)存區(qū)域來(lái)描繪它的虛擬空間,如何關(guān)聯(lián)這些不同的內(nèi)存區(qū)域呢?大家可能都會(huì)想到使用鏈表,確實(shí)vm_area_struct構(gòu)造確實(shí)是以鏈表形式鏈接,不過(guò)為了方便查找,內(nèi)核又以紅黑樹(shù)以前的內(nèi)核使用平衡樹(shù)的形式組織內(nèi)存區(qū)域,以便降低搜

19、索耗時(shí)。并存的兩種組織形式,并非冗余:鏈表用于需要遍歷全部節(jié)點(diǎn)的時(shí)候用,而紅黑樹(shù)適用于在地址空間中定位特定內(nèi)存區(qū)域的時(shí)候。內(nèi)核為了內(nèi)存區(qū)域上的各種不同操作都能獲得高性能,所以同時(shí)使用了這兩種數(shù)據(jù)構(gòu)造。以下圖反映了進(jìn)程地址空間的管理模型:進(jìn)程的地址空間對(duì)應(yīng)的描繪構(gòu)造是"內(nèi)存描繪符構(gòu)造",它表示進(jìn)程的全部地址空間,-包含了和進(jìn)程地址空間有關(guān)的全部信息,其中當(dāng)然包含進(jìn)程的內(nèi)存區(qū)域。進(jìn)程內(nèi)存的分配與回收創(chuàng)立進(jìn)程fork、程序載入execve、映射文件mmap、動(dòng)態(tài)內(nèi)存分配malloc/brk等進(jìn)程相關(guān)操作都需要分配內(nèi)存給進(jìn)程。不過(guò)這時(shí)進(jìn)程申請(qǐng)和獲得的還不是實(shí)際內(nèi)存,而是虛擬內(nèi)存,準(zhǔn)

20、確的說(shuō)是"內(nèi)存區(qū)域"。進(jìn)程對(duì)內(nèi)存區(qū)域的分配最終都會(huì)歸結(jié)到do_mmap函數(shù)上來(lái)brk調(diào)用被單獨(dú)以系統(tǒng)調(diào)用實(shí)現(xiàn),不用do_mmap,內(nèi)核使用do_mmap函數(shù)創(chuàng)立一個(gè)新的線性地址區(qū)間。但是說(shuō)該函數(shù)創(chuàng)立了一個(gè)新VMA并不非常準(zhǔn)確,因?yàn)榧偃鐒?chuàng)立的地址區(qū)間和一個(gè)已經(jīng)存在的地址區(qū)間相鄰,并且它們具有一樣的訪問(wèn)權(quán)限的話,那么兩個(gè)區(qū)間將合并為一個(gè)。假如不能合并,那么就確實(shí)需要?jiǎng)?chuàng)立一個(gè)新的VMA了。但無(wú)論哪種情況,do_mmap函數(shù)都會(huì)將一個(gè)地址區(qū)間參加到進(jìn)程的地址空間中-無(wú)論是擴(kuò)展已存在的內(nèi)存區(qū)域還是創(chuàng)立一個(gè)新的區(qū)域。同樣,釋放一個(gè)內(nèi)存區(qū)域應(yīng)使用函數(shù)do_ummap,它會(huì)銷(xiāo)毀對(duì)應(yīng)的內(nèi)存區(qū)

21、域。如何由虛變實(shí)!從上面已經(jīng)看到進(jìn)程所能直接操作的地址都為虛擬地址。當(dāng)進(jìn)程需要內(nèi)存時(shí),從內(nèi)核獲得的僅僅是虛擬的內(nèi)存區(qū)域,而不是實(shí)際的物理地址,進(jìn)程并沒(méi)有獲得物理內(nèi)存物理頁(yè)面-頁(yè)的概念請(qǐng)大家參考硬件根底一章,獲得的僅僅是對(duì)一個(gè)新的線性地址區(qū)間的使用權(quán)。實(shí)際的物理內(nèi)存只有當(dāng)進(jìn)程真的去訪問(wèn)新獲取的虛擬地址時(shí),才會(huì)由"懇求頁(yè)機(jī)制"產(chǎn)生"缺頁(yè)"異常,從而進(jìn)入分配實(shí)際頁(yè)面的例程。該異常是虛擬內(nèi)存機(jī)制賴以存在的根本保證-它會(huì)告訴內(nèi)核去真正為進(jìn)程分配物理頁(yè),并建立對(duì)應(yīng)的頁(yè)表,這之后虛擬地址才實(shí)實(shí)在在地映射到了系統(tǒng)的物理內(nèi)存上。當(dāng)然,假如頁(yè)被換出到磁盤(pán),也會(huì)產(chǎn)生缺頁(yè)異常,

22、不過(guò)這時(shí)不用再建立頁(yè)表了這種懇求頁(yè)機(jī)制把頁(yè)面的分配推延到不能再推延為止,并不急于把所有的事情都一次做完這種思想有點(diǎn)像設(shè)計(jì)形式中的代理形式proxy。之所以能這么做是利用了內(nèi)存訪問(wèn)的"部分性原理",懇求頁(yè)帶來(lái)的好處是節(jié)約了空閑內(nèi)存,進(jìn)步了系統(tǒng)的吞吐率。要想更清楚地理解懇求頁(yè)機(jī)制,可以看看?深化理解linux內(nèi)核?一書(shū)。這里我們需要說(shuō)明在內(nèi)存區(qū)域構(gòu)造上的nopage操作。當(dāng)訪問(wèn)的進(jìn)程虛擬內(nèi)存并未真正分配頁(yè)面時(shí),該操作便被調(diào)用來(lái)分配實(shí)際的物理頁(yè),并為該頁(yè)建立頁(yè)表項(xiàng)。在最后的例子中我們會(huì)演示如何使用該方法。系統(tǒng)物理內(nèi)存管理雖然應(yīng)用程序操作的對(duì)象是映射到物理內(nèi)存之上的虛擬內(nèi)存,但是處

23、理器直接操作的卻是物理內(nèi)存。所以當(dāng)應(yīng)用程序訪問(wèn)一個(gè)虛擬地址時(shí),首先必須將虛擬地址轉(zhuǎn)化成物理地址,然后處理器才能解析地址訪問(wèn)懇求。地址的轉(zhuǎn)換工作需要通過(guò)查詢頁(yè)表才能完成,概括地講,地址轉(zhuǎn)換需要將虛擬地址分段,使每段虛地址都作為一個(gè)索引指向頁(yè)表,而頁(yè)表項(xiàng)那么指向下一級(jí)別的頁(yè)表或者指向最終的物理頁(yè)面。每個(gè)進(jìn)程都有自己的頁(yè)表。進(jìn)程描繪符的pgd域指向的就是進(jìn)程的頁(yè)全局目錄。下面我們借用?linux設(shè)備驅(qū)動(dòng)程序?中的一幅圖大致看看進(jìn)程地址空間到物理頁(yè)之間的轉(zhuǎn)換關(guān)系。上面的過(guò)程說(shuō)起來(lái)簡(jiǎn)單,做起來(lái)難呀。因?yàn)樵谔摂M地址映射到頁(yè)之前必須先分配物理頁(yè)-也就是說(shuō)必須先從內(nèi)核中獲取空閑頁(yè),并建立頁(yè)表。下面我們介紹一下

24、內(nèi)核管理物理內(nèi)存的機(jī)制。物理內(nèi)存管理頁(yè)管理Linux內(nèi)核管理物理內(nèi)存是通過(guò)分頁(yè)機(jī)制實(shí)現(xiàn)的,它將整個(gè)內(nèi)存劃分成無(wú)數(shù)個(gè)4k在i386體系構(gòu)造中大小的頁(yè),從而分配和回收內(nèi)存的根本單位便是內(nèi)存頁(yè)了。利用分頁(yè)管理有助于靈敏分配內(nèi)存地址,因?yàn)榉峙鋾r(shí)不必要求必須有大塊的連續(xù)內(nèi)存,系統(tǒng)可以東一頁(yè)、西一頁(yè)的湊出所需要的內(nèi)存供進(jìn)程使用。雖然如此,但是實(shí)際上系統(tǒng)使用內(nèi)存時(shí)還是傾向于分配連續(xù)的內(nèi)存塊,因?yàn)榉峙溥B續(xù)內(nèi)存時(shí),頁(yè)表不需要更改,因此能降低TLB的刷新率頻繁刷新會(huì)在很大程度上降低訪問(wèn)速度。鑒于上述需求,內(nèi)核分配物理頁(yè)面時(shí)為了盡量減少不連續(xù)情況,采用了"伙伴"關(guān)系來(lái)管理空閑頁(yè)面?;锇殛P(guān)系分配算

25、法大家應(yīng)該不陌生-幾乎所有操作系統(tǒng)方面的書(shū)都會(huì)提到,我們不去詳細(xì)說(shuō)它了,假如不明白可以參看有關(guān)資料。這里只需要大家明白Linux中空閑頁(yè)面的組織和管理利用了伙伴關(guān)系,因此空閑頁(yè)面分配時(shí)也需要遵循伙伴關(guān)系,最小單位只能是2的冪倍頁(yè)面大小。內(nèi)核中分配空閑頁(yè)面的根本函數(shù)是get_free_page/get_free_pages,它們或是分配單頁(yè)或是分配指定的頁(yè)面2、4、8512頁(yè)。get_free_page是在內(nèi)核中分配內(nèi)存,不同于malloc在用戶空間中分配,malloc利用堆動(dòng)態(tài)分配,實(shí)際上是調(diào)用brk系統(tǒng)調(diào)用,該調(diào)用的作用是擴(kuò)大或縮小進(jìn)程堆空間它會(huì)修改進(jìn)程的brk域。假如現(xiàn)有的內(nèi)存區(qū)域不夠包容

26、堆空間,那么會(huì)以頁(yè)面大小的倍數(shù)為單位,擴(kuò)張或收縮對(duì)應(yīng)的內(nèi)存區(qū)域,但brk值并非以頁(yè)面大小為倍數(shù)修改,而是按實(shí)際懇求修改。因此Malloc在用戶空間分配內(nèi)存可以以字節(jié)為單位分配,但內(nèi)核在內(nèi)部仍然會(huì)是以頁(yè)為單位分配的。另外,需要提及的是,物理頁(yè)在系統(tǒng)中由頁(yè)構(gòu)造structpage描繪,系統(tǒng)中所有的頁(yè)面都存儲(chǔ)在數(shù)組mem_map中,可以通過(guò)該數(shù)組找到系統(tǒng)中的每一頁(yè)空閑或非空閑。而其中的空閑頁(yè)面那么可由上述提到的以伙伴關(guān)系組織的空閑頁(yè)鏈表free_areaMAX_ORDER來(lái)索引。內(nèi)核內(nèi)存使用Slab所謂尺有所長(zhǎng),寸有所短。以頁(yè)為最小單位分配內(nèi)存對(duì)于內(nèi)核管理系統(tǒng)中的物理內(nèi)存來(lái)說(shuō)確實(shí)比較方便,但內(nèi)核自身

27、最常使用的內(nèi)存卻往往是很小遠(yuǎn)遠(yuǎn)小于一頁(yè)的內(nèi)存塊-比方存放文件描繪符、進(jìn)程描繪符、虛擬內(nèi)存區(qū)域描繪符等行為所需的內(nèi)存都缺乏一頁(yè)。這些用來(lái)存放描繪符的內(nèi)存相比頁(yè)面而言,就好比是面包屑與面包。一個(gè)整頁(yè)中可以聚集多個(gè)這些小塊內(nèi)存;而且這些小塊內(nèi)存塊也和面包屑一樣頻繁地生成/銷(xiāo)毀。為了滿足內(nèi)核對(duì)這種小內(nèi)存塊的需要,Linux系統(tǒng)采用了一種被稱為slab分配器的技術(shù)。Slab分配器的實(shí)現(xiàn)相當(dāng)復(fù)雜,但原理不難,其核心思想就是"存儲(chǔ)池"的運(yùn)用。內(nèi)存片段小塊內(nèi)存被看作對(duì)象,當(dāng)被使用完后,并不直接釋放而是被緩存到"存儲(chǔ)池"里,留做下次使用,這無(wú)疑防止了頻繁創(chuàng)立與銷(xiāo)毀對(duì)象所帶

28、來(lái)的額外負(fù)載。Slab技術(shù)不但防止了內(nèi)存內(nèi)部分片下文將解釋帶來(lái)的不便引入Slab分配器的主要目的是為了減少對(duì)伙伴系統(tǒng)分配算法的調(diào)用次數(shù)-頻繁分配和回收必然會(huì)導(dǎo)致內(nèi)存碎片-難以找到大塊連續(xù)的可用內(nèi)存,而且可以很好地利用硬件緩存進(jìn)步訪問(wèn)速度。Slab并非是脫離伙伴關(guān)系而獨(dú)立存在的一種內(nèi)存分配方式,slab仍然是建立在頁(yè)面根底之上,換句話說(shuō),Slab將頁(yè)面來(lái)自于伙伴關(guān)系管理的空閑頁(yè)面鏈表撕碎成眾多小內(nèi)存塊以供分配,slab中的對(duì)象分配和銷(xiāo)毀使用kmem_cache_alloc與kmem_cache_free。Kmalloc Slab分配器不僅僅只用來(lái)存放內(nèi)核專用的構(gòu)造體,它還被用來(lái)處理內(nèi)核對(duì)小塊內(nèi)存

29、的懇求。當(dāng)然鑒于Slab分配器的特點(diǎn),一般來(lái)說(shuō)內(nèi)核程序中對(duì)小于一頁(yè)的小塊內(nèi)存的懇求才通過(guò)Slab分配器提供的接口Kmalloc來(lái)完成雖然它可分配32到131072字節(jié)的內(nèi)存。從內(nèi)核內(nèi)存分配的角度來(lái)講,kmalloc可被看成是get_free_pages的一個(gè)有效補(bǔ)充,內(nèi)存分配粒度更靈敏了。有興趣的話,可以到/proc/slabinfo中找到內(nèi)核執(zhí)行現(xiàn)場(chǎng)使用的各種slab信息統(tǒng)計(jì),其中你會(huì)看到系統(tǒng)中所有slab的使用信息。從信息中可以看到系統(tǒng)中除了專用構(gòu)造體使用的slab外,還存在大量為Kmalloc而準(zhǔn)備的Slab其中有些為dma準(zhǔn)備的。內(nèi)核非連續(xù)內(nèi)存分配Vmalloc伙伴關(guān)系也好、slab技

30、術(shù)也好,從內(nèi)存管理理論角度而言目的根本是一致的,它們都是為了防止"分片",不過(guò)分片又分為外部分片和內(nèi)部分片之說(shuō),所謂內(nèi)部分片是說(shuō)系統(tǒng)為了滿足一小段內(nèi)存區(qū)連續(xù)的需要,不得不分配了一大區(qū)域連續(xù)內(nèi)存給它,從而造成了空間浪費(fèi);外部分片是指系統(tǒng)雖有足夠的內(nèi)存,但卻是分散的碎片,無(wú)法滿足對(duì)大塊"連續(xù)內(nèi)存"的需求。無(wú)論何種分片都是系統(tǒng)有效利用內(nèi)存的障礙。slab分配器使得一個(gè)頁(yè)面內(nèi)包含的眾多小塊內(nèi)存可獨(dú)立被分配使用,防止了內(nèi)部分片,節(jié)約了空閑內(nèi)存?;锇殛P(guān)系把內(nèi)存塊按大小分組管理,一定程度上減輕了外部分片的危害,因?yàn)轫?yè)框分配不在盲目,而是按照大小依次有序進(jìn)展,不過(guò)伙伴關(guān)

31、系只是減輕了外部分片,但并未徹底消除。你自己比劃一下屢次分配頁(yè)面后,空閑內(nèi)存的剩余情況吧。所以防止外部分片的最終思路還是落到了如何利用不連續(xù)的內(nèi)存塊組合成"看起來(lái)很大的內(nèi)存塊"-這里的情況很類似于用戶空間分配虛擬內(nèi)存,內(nèi)存邏輯上連續(xù),其實(shí)映射到并不一定連續(xù)的物理內(nèi)存上。Linux內(nèi)核借用了這個(gè)技術(shù),允許內(nèi)核程序在內(nèi)核地址空間中分配虛擬地址,同樣也利用頁(yè)表內(nèi)核頁(yè)表將虛擬地址映射到分散的內(nèi)存頁(yè)上。以此完美地解決了內(nèi)核內(nèi)存使用中的外部分片問(wèn)題。內(nèi)核提供vmalloc函數(shù)分配內(nèi)核虛擬內(nèi)存,該函數(shù)不同于kmalloc,它可以分配較Kmalloc大得多的內(nèi)存空間可遠(yuǎn)大于128K,但必須

32、是頁(yè)大小的倍數(shù),但相比Kmalloc來(lái)說(shuō),Vmalloc需要對(duì)內(nèi)核虛擬地址進(jìn)展重映射,必須更新內(nèi)核頁(yè)表,因此分配效率上要低一些用空間換時(shí)間與用戶進(jìn)程相似,內(nèi)核也有一個(gè)名為init_mm的mm_strcut構(gòu)造來(lái)描繪內(nèi)核地址空間,其中頁(yè)表項(xiàng)pdg=swapper_pg_dir包含了系統(tǒng)內(nèi)核空間3G-4G的映射關(guān)系。因此vmalloc分配內(nèi)核虛擬地址必須更新內(nèi)核頁(yè)表,而kmalloc或get_free_page由于分配的連續(xù)內(nèi)存,所以不需要更新內(nèi)核頁(yè)表。malloc分配的內(nèi)核虛擬內(nèi)存與kmalloc/get_free_page分配的內(nèi)核虛擬內(nèi)存位于不同的區(qū)間,不會(huì)重疊。因?yàn)閮?nèi)核虛擬空間被分區(qū)管理,

33、各司其職。進(jìn)程空間地址分布從0到3G其實(shí)是到PAGE_OFFSET,在0x86中它等于0xC 0000000,從3G到vmalloc_start這段地址是物理內(nèi)存映射區(qū)域該區(qū)域中包含了內(nèi)核鏡像、物理頁(yè)面表mem_map等等比方我使用的系統(tǒng)內(nèi)存是64M可以用free看到,那么3G-3G+64M這片內(nèi)存就應(yīng)該映射到物理內(nèi)存,而vmalloc_start位置應(yīng)在3G+64M附近說(shuō)"附近"因?yàn)槭窃谖锢韮?nèi)存映射區(qū)與vmalloc_start期間還會(huì)存在一個(gè)8M大小的gap來(lái)防止躍界,vmalloc_end的位置接近4G說(shuō)"接近"是因?yàn)樽詈笪恢孟到y(tǒng)會(huì)保存一片128k

34、大小的區(qū)域用于專用頁(yè)面映射,還有可能會(huì)有高端內(nèi)存映射區(qū),這些都是細(xì)節(jié),這里我們不做糾纏。上圖是內(nèi)存分布的模糊輪廓由get_free_page或Kmalloc函數(shù)所分配的連續(xù)內(nèi)存都陷于物理映射區(qū)域,所以它們返回的內(nèi)核虛擬地址和實(shí)際物理地址僅僅是相差一個(gè)偏移量PAGE_OFFSET,你可以很方便的將其轉(zhuǎn)化為物理內(nèi)存地址,同時(shí)內(nèi)核也提供了virt_to_phys函數(shù)將內(nèi)核虛擬空間中的物理映射區(qū)地址轉(zhuǎn)化為物理地址。要知道,物理內(nèi)存映射區(qū)中的地址與內(nèi)核頁(yè)表是有序?qū)?yīng)的,系統(tǒng)中的每個(gè)物理頁(yè)面都可以找到它對(duì)應(yīng)的內(nèi)核虛擬地址在物理內(nèi)存映射區(qū)中的。而vmalloc分配的地址那么限于vmalloc_start與v

35、malloc_end之間。每一塊vmalloc分配的內(nèi)核虛擬內(nèi)存都對(duì)應(yīng)一個(gè)vm_struct構(gòu)造體可別和vm_area_struct搞混,那可是進(jìn)程虛擬內(nèi)存區(qū)域的構(gòu)造,不同的內(nèi)核虛擬地址被4k大小的空閑區(qū)間隔,以防止越界-見(jiàn)以下圖。與進(jìn)程虛擬地址的特性一樣,這些虛擬地址與物理內(nèi)存沒(méi)有簡(jiǎn)單的位移關(guān)系,必須通過(guò)內(nèi)核頁(yè)表才可轉(zhuǎn)換為物理地址或物理頁(yè)。它們有可能尚未被映射,在發(fā)生缺頁(yè)時(shí)才真正分配物理頁(yè)面。這里給出一個(gè)小程序幫助大家認(rèn)清上面幾種分配函數(shù)所對(duì)應(yīng)的區(qū)域。includelinux/module.h includelinux/slab.h includelinux/vmalloc.h unsign

36、ed char*pagemem;unsigned char*kmallocmem;unsigned char*vmallocmem;int init_modulevoidpagemem=get_free_page0;printk"1 pagemem=%s",pagemem;kmallocmem=kmalloc100,0;printk"1 kmallocmem=%s",kmallocmem;vmallocmem=vmalloc 1000000;printk"1 vmallocmem=%s",vmallocmem;void cleanup

37、_modulevoidfree_pagepagemem;kfreekmallocmem;vfreevmallocmem;實(shí)例內(nèi)存映射mmap是Linux操作系統(tǒng)的一個(gè)很大特色,它可以將系統(tǒng)內(nèi)存映射到一個(gè)文件設(shè)備上,以便可以通過(guò)訪問(wèn)文件內(nèi)容來(lái)到達(dá)訪問(wèn)內(nèi)存的目的。這樣做的最大好處是進(jìn)步了內(nèi)存訪問(wèn)速度,并且可以利用文件系統(tǒng)的接口編程設(shè)備在Linux中作為特殊文件處理訪問(wèn)內(nèi)存,降低了開(kāi)發(fā)難度。許多設(shè)備驅(qū)動(dòng)程序便是利用內(nèi)存映射功能將用戶空間的一段地址關(guān)聯(lián)到設(shè)備內(nèi)存上,無(wú)論何時(shí),只要內(nèi)存在分配的地址范圍內(nèi)進(jìn)展讀寫(xiě),實(shí)際上就是對(duì)設(shè)備內(nèi)存的訪問(wèn)。同時(shí)對(duì)設(shè)備文件的訪問(wèn)也等同于對(duì)內(nèi)存區(qū)域的訪問(wèn),也就是說(shuō),通過(guò)文件

38、操作接口可以訪問(wèn)內(nèi)存。Linux中的X效勞器就是一個(gè)利用內(nèi)存映射到達(dá)直接高速訪問(wèn)視頻卡內(nèi)存的例子。熟悉文件操作的朋友一定會(huì)知道file_operations構(gòu)造中有mmap方法,在用戶執(zhí)行mmap系統(tǒng)調(diào)用時(shí),便會(huì)調(diào)用該方法來(lái)通過(guò)文件訪問(wèn)內(nèi)存-不過(guò)在調(diào)用文件系統(tǒng)mmap方法前,內(nèi)核還需要處理分配內(nèi)存區(qū)域vma_struct、建立頁(yè)表等工作。對(duì)于詳細(xì)映射細(xì)節(jié)不作介紹了,需要強(qiáng)調(diào)的是,建立頁(yè)表可以采用remap_page_range方法一次建立起所有映射區(qū)的頁(yè)表,或利用vma_struct的nopage方法在缺頁(yè)時(shí)現(xiàn)場(chǎng)一頁(yè)一頁(yè)的建立頁(yè)表。第一種方法相比第二種方法簡(jiǎn)單方便、速度快,但是靈敏性不高。一次調(diào)用所有頁(yè)表便定型了,不適用于那些需要現(xiàn)場(chǎng)建立頁(yè)表的場(chǎng)合-比方映射區(qū)需要擴(kuò)展或下面我們例子中的情況。我們這里的實(shí)例希望利用內(nèi)存映射,將系統(tǒng)內(nèi)核中的一部分虛擬內(nèi)存映射到用戶空間,以供給用程序讀取-你可利用它進(jìn)展內(nèi)核空間到用戶空間的大規(guī)模信息傳輸。因此我們將試圖寫(xiě)一個(gè)虛擬字符設(shè)備驅(qū)動(dòng)程序,通過(guò)它將系統(tǒng)內(nèi)

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論