溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

怎么搞懂Linux內(nèi)存管理

發(fā)布時間:2021-11-02 09:30:21 來源:億速云 閱讀:143 作者:柒染 欄目:系統(tǒng)運(yùn)維

今天就跟大家聊聊有關(guān)怎么搞懂Linux內(nèi)存管理,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。

內(nèi)存管理應(yīng)該是Linux內(nèi)核中非常重要的子系統(tǒng),之前一直在構(gòu)思怎么去寫一篇Linux內(nèi)存管理的文章,由于內(nèi)容實(shí)在過于龐大復(fù)雜,想要通俗易懂而且不丟失專業(yè)性的闡述真的是一種考驗(yàn)。了解內(nèi)管管理的實(shí)現(xiàn)原理不管對內(nèi)核開發(fā)人員還是應(yīng)用程序開發(fā)人員來說都幫助極大。本文也致力于用簡單生動的語言帶領(lǐng)大家認(rèn)識內(nèi)存管理的原理,當(dāng)然也少不了一些理論知識的鋪墊。我們的目的不是探討理論,而是為了更加全面的理解原理,必要時我們會深入理論,窺探理論知識的背后。

進(jìn)程和內(nèi)存

我們都知道,進(jìn)程運(yùn)行需要內(nèi)存。它主要是用來存放從存儲介質(zhì)中(磁盤/flash/...)載入的程序代碼和進(jìn)程運(yùn)行所需要的數(shù)據(jù)內(nèi)容。在我的另一篇文章中怎樣深入理解堆和棧有對進(jìn)程的組成講解。對于一個進(jìn)程來說都會有5中不同的數(shù)據(jù)段。

  • 代碼段(text):代碼段是用來存放可執(zhí)行文件的操作指令,也就是說它存放的是可執(zhí)行程序中在內(nèi)存中的鏡像。代碼段是不允許修改的,所以只能進(jìn)行讀操作,而不允許寫入的操作。

  • 數(shù)據(jù)段(data):數(shù)據(jù)段主要用來存放已經(jīng)初始化的全局變量,也就是說存放程序靜態(tài)分配的變量(靜態(tài)分配內(nèi)存就是編譯器在編譯程序的時候根據(jù)源程序來分配內(nèi)存.  動態(tài)分配內(nèi)存就是在程序編譯之后, 運(yùn)行時調(diào)用運(yùn)行時刻庫函數(shù)來分配內(nèi)存的. 靜態(tài)分配由于是在程序運(yùn)行之前,所以速度快, 效率高, 但是局限性大.  動態(tài)分配在程序運(yùn)行時執(zhí)行, 所以速度慢, 但靈活性高.)和全局變量。

  • bss段:bss段包含了程序中未初始化的全局變量,在內(nèi)存中bss段會全部統(tǒng)一清零。(延伸:這就是為什么沒有初始化的全局變量,都會被清零的原因)

  • 堆(heap):堆是用來存儲進(jìn)程動態(tài)分配的內(nèi)存,它的大小并不固定。具體可參考怎樣深入理解堆和棧

  • 棧(stack):棧是用來存放臨時變量的地方,也就是C程序中{}中的變量,不包括static聲明的變量(雖然static是局部變量,它的作用范圍在{}中,但是它的生存周期是整個程序生命周期,它存放在數(shù)據(jù)段中)。程序在函數(shù)調(diào)用時,參數(shù)個數(shù)過多的函數(shù)會通過棧的方式,將參數(shù)壓入棧中,并且在調(diào)用結(jié)束后,函數(shù)的返回值也會通過棧來返回。從這個意義上講,我們可以把??闯梢粋€寄存,交換臨時數(shù)據(jù)的內(nèi)存區(qū)。詳細(xì)可以參考文章怎樣深入理解堆和棧。

通過程序?qū)?nèi)存的不同用途,分為了上述5種不同的段,那這些段在內(nèi)存是怎樣組織的呢?看下圖:

怎么搞懂Linux內(nèi)存管理

從圖中我們不難發(fā)現(xiàn),堆棧好像是挨在一起的,他們一個向下“長”(i386體系結(jié)構(gòu)中棧向下、堆向上),一個向上“長”,相對而生。但你不必?fù)?dān)心他們會碰頭,因?yàn)樗麄冎g間隔真的很大。

從用戶態(tài)向內(nèi)核態(tài)看,我們所使用的內(nèi)存形式的變化:

怎么搞懂Linux內(nèi)存管理

邏輯地址經(jīng)段機(jī)制轉(zhuǎn)化成線性地址;線性地址又經(jīng)過頁機(jī)制轉(zhuǎn)化為物理地址。(但是我們要知道Linux系統(tǒng)雖然保留了段機(jī)制,但是將所有程序的段地址都定死為0-4G,所以雖然邏輯地址和線性地址是兩種不同的地址空間,但在Linux中邏輯地址就等于線性地址,它們的值是一樣的)。沿著這條線索,我們所研究的主要問題也就集中在下面幾個問題。

  • 進(jìn)程地址空間如何管理?

  • 進(jìn)程地址如何映射物理內(nèi)存呢?

  • 物理內(nèi)存又是如何被管理的呢?

下面我們就來看看吧。

進(jìn)程地址空間

現(xiàn)代的操作系統(tǒng)基本是采用虛擬內(nèi)存管理技術(shù),當(dāng)然Linux作為先進(jìn)的os也不例外,每個進(jìn)程都有自己的進(jìn)程地址空間。該空間為4G的線性虛擬空間。用戶態(tài)接觸到的都是虛擬地址,根本無法看到物理地址,也不用關(guān)心物理地址。利用這種虛擬地址的方式,可以保護(hù)內(nèi)存資源,起到隔離的作用。而且對于用戶程序來說,始終是4G的大小,可以在程序編譯的時候就能確定代碼段地址。我們應(yīng)該知道三件事情:

  • 4G的進(jìn)程地址空間被人為的分為兩個部分——用戶空間與內(nèi)核空間。用戶空間從0到3G(0xC0000000),內(nèi)核空間占據(jù)3G到4G。用戶進(jìn)程通常情況下只能訪問用戶空間的虛擬地址,不能訪問內(nèi)核空間虛擬地址。只有用戶進(jìn)程進(jìn)行系統(tǒng)調(diào)用(代表用戶進(jìn)程在內(nèi)核態(tài)執(zhí)行)等時刻可以訪問到內(nèi)核空間。

  • 每當(dāng)進(jìn)程切換,用戶空間就會變化,而內(nèi)核空間是內(nèi)核負(fù)責(zé)映射的。它不隨著進(jìn)程的變化而變化。內(nèi)核空間有自己的對應(yīng)的頁表(init_mm.pgd),用戶進(jìn)程有各自的頁表。

  • 每個進(jìn)程的用戶空間都是獨(dú)立的。

進(jìn)程內(nèi)存管理

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

如果你要查看某個進(jìn)程占用的內(nèi)存區(qū)域,可以使用命令cat /proc//maps獲得(pid是進(jìn)程號),你會發(fā)現(xiàn)如下所示列表:

08048000 - 08049000 r-xp 00000000 03:03 439029 /home/mm/src/example  08049000 - 0804a000 rw-p 00000000 03:03 439029 /home/mm/src/example  ……………  bfffe000 - c0000000 rwxp ffff000 00:00 0

每行數(shù)據(jù)格式如下:

(內(nèi)存區(qū)域)開始-結(jié)束 訪問權(quán)限 偏移 主設(shè)備號:次設(shè)備號 i節(jié)點(diǎn) 文件。

注意點(diǎn):你一定會發(fā)現(xiàn)進(jìn)程空間只包含三個內(nèi)存區(qū)域,似乎沒有上面所提到的堆、bss等,其實(shí)并非如此,程序內(nèi)存段和進(jìn)程地址空間中的內(nèi)存區(qū)域是種模糊對應(yīng),也就是說,堆、bss、數(shù)據(jù)段(初始化過的)都在進(jìn)程空間中由數(shù)據(jù)段內(nèi)存區(qū)域表示。

在Linux內(nèi)核中表示內(nèi)存區(qū)域的數(shù)據(jù)結(jié)構(gòu)是vm_area_struct,內(nèi)核將每個內(nèi)存區(qū)域作為單獨(dú)的內(nèi)存對象管理。采用面向?qū)ο蠓椒ㄊ筕MA結(jié)構(gòu)體可以代表多種類型的內(nèi)存區(qū)域,包括內(nèi)存映射文件和進(jìn)程用戶空間棧等等,這些區(qū)域的操作方法也不盡相同。

vm_area_strcut結(jié)構(gòu)比較復(fù)雜,關(guān)于它的詳細(xì)結(jié)構(gòu)請參閱相關(guān)資料。我們這里只對它的組織方法做一點(diǎn)補(bǔ)充說明。vm_area_struct是描述進(jìn)程地址空間的基本管理單元,對于一個進(jìn)程來說往往需要多個內(nèi)存區(qū)域來描述它的虛擬空間,如何關(guān)聯(lián)這些不同的內(nèi)存區(qū)域呢?大家可能都會想到使用鏈表,的確vm_area_struct結(jié)構(gòu)確實(shí)是以鏈表形式鏈接,不過為了方便查找,內(nèi)核又以紅黑樹(以前的內(nèi)核使用平衡樹)的形式組織內(nèi)存區(qū)域,以便降低搜索耗時。并存的兩種組織形式,并非冗余:鏈表用于需要遍歷全部節(jié)點(diǎn)的時候用,而紅黑樹適用于在地址空間中定位特定內(nèi)存區(qū)域的時候。內(nèi)核為了內(nèi)存區(qū)域上的各種不同操作都能獲得高性能,所以同時使用了這兩種數(shù)據(jù)結(jié)構(gòu)。

下圖反映了進(jìn)程地址空間的管理模型:

怎么搞懂Linux內(nèi)存管理

進(jìn)程的地址空間對應(yīng)的描述結(jié)構(gòu)是“內(nèi)存描述結(jié)構(gòu)”,它表示進(jìn)程的全部地址空間,包含了和進(jìn)程地址空間有關(guān)的全部信息,其中當(dāng)然包含進(jìn)程的內(nèi)存區(qū)域。

進(jìn)程內(nèi)存到底是怎樣分配與回收?

我們知道的一些系統(tǒng)調(diào)用,例如:創(chuàng)建進(jìn)程fork(),程序載入execve(),映射文件mmap(),動態(tài)內(nèi)存分配brk()等等都是需要分配內(nèi)存給進(jìn)程。但是這時進(jìn)程獲取的還不是實(shí)際物理的內(nèi)存,只是虛擬內(nèi)存,其實(shí)在內(nèi)核中只是表示的是“內(nèi)存區(qū)域”。進(jìn)程對內(nèi)存區(qū)域的分配最終是在內(nèi)核中的do_mmap()函數(shù)上執(zhí)行的(brk除外)。

內(nèi)核使用do_mmap()函數(shù)創(chuàng)建一個新的線性地址區(qū)間。然后會將一個地址區(qū)間加入到進(jìn)程的地址空間中,可能是創(chuàng)建一個新的區(qū)域或者是擴(kuò)展以存在的內(nèi)存區(qū)域。當(dāng)然釋放對應(yīng)的內(nèi)存區(qū)域是使用函數(shù)do_ummap()。

內(nèi)存如何由虛變實(shí)呢?

從上面已經(jīng)看到進(jìn)程所能直接操作的地址都為虛擬地址。當(dāng)進(jìn)程需要內(nèi)存時,從內(nèi)核獲得的僅僅是虛擬的內(nèi)存區(qū)域,而不是實(shí)際的物理地址,進(jìn)程并沒有獲得物理內(nèi)存(物理頁面——頁的概念請大家參考硬件基礎(chǔ)一章),獲得的僅僅是對一個新的線性地址區(qū)間的使用權(quán)。實(shí)際的物理內(nèi)存只有當(dāng)進(jìn)程真的去訪問新獲取的虛擬地址時,才會由“請求頁機(jī)制”產(chǎn)生“缺頁”異常,從而進(jìn)入分配實(shí)際頁面的函數(shù)。

該異常是虛擬內(nèi)存機(jī)制賴以存在的基本保證——它會告訴內(nèi)核去真正為進(jìn)程分配物理頁,并建立對應(yīng)的頁表,這之后虛擬地址才實(shí)實(shí)在在地映射到了系統(tǒng)的物理內(nèi)存上。(當(dāng)然,如果頁被換出到磁盤,也會產(chǎn)生缺頁異常,不過這時不用再建立頁表了)

這種請求頁機(jī)制把頁面的分配推遲到不能再推遲為止,并不急于把所有的事情都一次做完(這種思想有點(diǎn)像設(shè)計模式中的代理模式(proxy))。之所以能這么做是利用了內(nèi)存訪問的“局部性原理”,請求頁帶來的好處是節(jié)約了空閑內(nèi)存,提高了系統(tǒng)的吞吐率。要想更清楚地了解請求頁機(jī)制,可以看看《深入理解linux內(nèi)核》一書。

這里我們需要說明在內(nèi)存區(qū)域結(jié)構(gòu)上的nopage操作。當(dāng)訪問的進(jìn)程虛擬內(nèi)存并未真正分配頁面時,該操作便被調(diào)用來分配實(shí)際的物理頁,并為該頁建立頁表項(xiàng)。在最后的例子中我們會演示如何使用該方法。

物理內(nèi)存怎樣管理?

雖然應(yīng)用程序操作的對象是映射到物理內(nèi)存之上的虛擬內(nèi)存,但是處理器直接操作的卻是物理內(nèi)存。所以當(dāng)應(yīng)用程序訪問一個虛擬地址時,首先必須將虛擬地址轉(zhuǎn)化成物理地址,然后處理器才能解析地址訪問請求。地址的轉(zhuǎn)換工作需要通過查詢頁表才能完成,概括地講,地址轉(zhuǎn)換需要將虛擬地址分段,使每段虛地址都作為一個索引指向頁表,而頁表項(xiàng)則指向下一級別的頁表或者指向最終的物理頁面。

每個進(jìn)程都有自己的頁表。進(jìn)程描述符的pgd域指向的就是進(jìn)程的頁全局目錄。下面我們借用《linux設(shè)備驅(qū)動程序》中的一幅圖大致看看進(jìn)程地址空間到物理頁之間的轉(zhuǎn)換關(guān)系。

怎么搞懂Linux內(nèi)存管理

上面的過程說起來簡單,做起來難呀。因?yàn)樵谔摂M地址映射到頁之前必須先分配物理頁——也就是說必須先從內(nèi)核中獲取空閑頁,并建立頁表。下面我們介紹一下內(nèi)核管理物理內(nèi)存的機(jī)制。

Linux內(nèi)核管理物理內(nèi)存是通過分頁機(jī)制實(shí)現(xiàn)的,它將整個內(nèi)存劃分成無數(shù)個4k(在i386體系結(jié)構(gòu)中)大小的頁,從而分配和回收內(nèi)存的基本單位便是內(nèi)存頁了。利用分頁管理有助于靈活分配內(nèi)存地址,因?yàn)榉峙鋾r不必要求必須有大塊的連續(xù)內(nèi)存,系統(tǒng)可以東一頁、西一頁的湊出所需要的內(nèi)存供進(jìn)程使用。雖然如此,但是實(shí)際上系統(tǒng)使用內(nèi)存時還是傾向于分配連續(xù)的內(nèi)存塊,因?yàn)榉峙溥B續(xù)內(nèi)存時,頁表不需要更改,因此能降低TLB的刷新率(頻繁刷新會在很大程度上降低訪問速度)。

鑒于上述需求,內(nèi)核分配物理頁面時為了盡量減少不連續(xù)情況,采用了“伙伴”關(guān)系來管理空閑頁面?;锇殛P(guān)系分配算法大家應(yīng)該不陌生,如果不明白可以參看有關(guān)資料。這里只需要大家明白Linux中空閑頁面的組織和管理利用了伙伴關(guān)系,因此空閑頁面分配時也需要遵循伙伴關(guān)系,最小單位只能是2的冪倍頁面大小。內(nèi)核中分配空閑頁面的基本函數(shù)是get_free_page/get_free_pages,它們或是分配單頁或是分配指定的頁面(2、4、8…512頁)。

注意:get_free_page是在內(nèi)核中分配內(nèi)存,不同于malloc在用戶空間中分配,malloc利用堆動態(tài)分配,實(shí)際上是調(diào)用brk()系統(tǒng)調(diào)用,該調(diào)用的作用是擴(kuò)大或縮小進(jìn)程堆空間(它會修改進(jìn)程的brk域)。如果現(xiàn)有的內(nèi)存區(qū)域不夠容納堆空間,則會以頁面大小的倍數(shù)為單位,擴(kuò)張或收縮對應(yīng)的內(nèi)存區(qū)域,但brk值并非以頁面大小為倍數(shù)修改,而是按實(shí)際請求修改。因此Malloc在用戶空間分配內(nèi)存可以以字節(jié)為單位分配,但內(nèi)核在內(nèi)部仍然會是以頁為單位分配的。

另外,需要說的是,物理頁在系統(tǒng)中由頁結(jié)構(gòu)struct  page描述,系統(tǒng)中所有的頁面都存儲在數(shù)組mem_map[]中,可以通過該數(shù)組找到系統(tǒng)中的每一頁(空閑或非空閑)。而其中的空閑頁面則可由上述提到的以伙伴關(guān)系組織的空閑頁鏈表(free_area[MAX_ORDER])來索引。

怎么搞懂Linux內(nèi)存管理

何為slab?

以頁為最小單位分配內(nèi)存對于內(nèi)核管理系統(tǒng)中的物理內(nèi)存來說的確比較方便,但內(nèi)核自身最常使用的內(nèi)存卻往往是很小(遠(yuǎn)遠(yuǎn)小于一頁)的內(nèi)存塊——比如存放文件描述符、進(jìn)程描述符、虛擬內(nèi)存區(qū)域描述符等行為所需的內(nèi)存都不足一頁。這些用來存放描述符的內(nèi)存相比頁面而言,就好比是面包屑與面包。一個整頁中可以聚集多個這些小塊內(nèi)存;而且這些小塊內(nèi)存塊也和面包屑一樣頻繁地生成/銷毀。

為了滿足內(nèi)核對這種小內(nèi)存塊的需要,Linux系統(tǒng)采用了一種被稱為slab分配器的技術(shù)。Slab分配器的實(shí)現(xiàn)相當(dāng)復(fù)雜,但原理不難,其核心思想就是“存儲池”的運(yùn)用。內(nèi)存片段(小塊內(nèi)存)被看作對象,當(dāng)被使用完后,并不直接釋放而是被緩存到“存儲池”里,留做下次使用,這無疑避免了頻繁創(chuàng)建與銷毀對象所帶來的額外負(fù)載。

Slab技術(shù)不但避免了內(nèi)存內(nèi)部分片)帶來的不便(引入Slab分配器的主要目的是為了減少對伙伴系統(tǒng)分配算法的調(diào)用次數(shù)——頻繁分配和回收必然會導(dǎo)致內(nèi)存碎片——難以找到大塊連續(xù)的可用內(nèi)存),而且可以很好地利用硬件緩存提高訪問速度。

Slab并非是脫離伙伴關(guān)系而獨(dú)立存在的一種內(nèi)存分配方式,slab仍然是建立在頁面基礎(chǔ)之上,換句話說,Slab將頁面(來自于伙伴關(guān)系管理的空閑頁面鏈表)撕碎成眾多小內(nèi)存塊以供分配,slab中的對象分配和銷毀使用kmem_cache_alloc與kmem_cache_free。

kmalloc()

lab分配器不僅僅只用來存放內(nèi)核專用的結(jié)構(gòu)體,它還被用來處理內(nèi)核對小塊內(nèi)存的請求。當(dāng)然鑒于Slab分配器的特點(diǎn),一般來說內(nèi)核程序中對小于一頁的小塊內(nèi)存的請求才通過Slab分配器提供的接口Kmalloc來完成(雖然它可分配32  到131072字節(jié)的內(nèi)存)。從內(nèi)核內(nèi)存分配的角度來講,kmalloc可被看成是get_free_page(s)的一個有效補(bǔ)充,內(nèi)存分配粒度更靈活了。

有興趣的話,可以到/proc/slabinfo中找到內(nèi)核執(zhí)行現(xiàn)場使用的各種slab信息統(tǒng)計,其中你會看到系統(tǒng)中所有slab的使用信息。從信息中可以看到系統(tǒng)中除了專用結(jié)構(gòu)體使用的slab外,還存在大量為Kmalloc而準(zhǔn)備的Slab(其中有些為dma準(zhǔn)備的)

vmalloc()

伙伴關(guān)系也好、slab技術(shù)也好,從內(nèi)存管理理論角度而言目的基本是一致的,它們都是為了防止“分片”,不過分片又分為外部分片和內(nèi)部分片之說,所謂內(nèi)部分片是說系統(tǒng)為了滿足一小段內(nèi)存區(qū)(連續(xù))的需要,不得不分配了一大區(qū)域連續(xù)內(nèi)存給它,從而造成了空間浪費(fèi);外部分片是指系統(tǒng)雖有足夠的內(nèi)存,但卻是分散的碎片,無法滿足對大塊“連續(xù)內(nèi)存”的需求。無論何種分片都是系統(tǒng)有效利用內(nèi)存的障礙。slab分配器使得一個頁面內(nèi)包含的眾多小塊內(nèi)存可獨(dú)立被分配使用,避免了內(nèi)部分片,節(jié)約了空閑內(nèi)存。伙伴關(guān)系把內(nèi)存塊按大小分組管理,一定程度上減輕了外部分片的危害,因?yàn)轫摽蚍峙洳辉诿つ?,而是按照大小依次有序進(jìn)行,不過伙伴關(guān)系只是減輕了外部分片,但并未徹底消除。你自己比劃一下多次分配頁面后,空閑內(nèi)存的剩余情況吧。

所以避免外部分片的最終思路還是落到了如何利用不連續(xù)的內(nèi)存塊組合成“看起來很大的內(nèi)存塊”——這里的情況很類似于用戶空間分配虛擬內(nèi)存,內(nèi)存邏輯上連續(xù),其實(shí)映射到并不一定連續(xù)的物理內(nèi)存上。Linux內(nèi)核借用了這個技術(shù),允許內(nèi)核程序在內(nèi)核地址空間中分配虛擬地址,同樣也利用頁表(內(nèi)核頁表)將虛擬地址映射到分散的內(nèi)存頁上。以此完美地解決了內(nèi)核內(nèi)存使用中的外部分片問題。內(nèi)核提供vmalloc函數(shù)分配內(nèi)核虛擬內(nèi)存,該函數(shù)不同于kmalloc,它可以分配較Kmalloc大得多的內(nèi)存空間(可遠(yuǎn)大于128K,但必須是頁大小的倍數(shù)),但相比Kmalloc來說,Vmalloc需要對內(nèi)核虛擬地址進(jìn)行重映射,必須更新內(nèi)核頁表,因此分配效率上要低一些(用空間換時間)。

vmalloc分配的內(nèi)核虛擬內(nèi)存與kmalloc/get_free_page分配的內(nèi)核虛擬內(nèi)存位于不同的區(qū)間,不會重疊。因?yàn)閮?nèi)核虛擬空間被分區(qū)管理,各司其職。進(jìn)程空間地址分布從0到3G(其實(shí)是到PAGE_OFFSET,  在0x86中它等于0xC0000000),從3G到vmalloc_start這段地址是物理內(nèi)存映射區(qū)域(該區(qū)域中包含了內(nèi)核鏡像、物理頁面表mem_map等等)比如我使用的系統(tǒng)內(nèi)存是64M(可以用free看到),那么(3G——3G+64M)這片內(nèi)存就應(yīng)該映射到物理內(nèi)存,而vmalloc_start位置應(yīng)在3G+64M附近(說"附近"因?yàn)槭窃谖锢韮?nèi)存映射區(qū)與vmalloc_start期間還會存在一個8M大小的gap來防止躍界),vmalloc_end的位置接近4G(說"接近"是因?yàn)樽詈笪恢孟到y(tǒng)會保留一片128k大小的區(qū)域用于專用頁面映射,還有可能會有高端內(nèi)存映射區(qū),這些都是細(xì)節(jié),這里我們不做糾纏)。

怎么搞懂Linux內(nèi)存管理

內(nèi)存分布的模糊輪廓

由get_free_page或Kmalloc函數(shù)所分配的連續(xù)內(nèi)存都陷于物理映射區(qū)域,所以它們返回的內(nèi)核虛擬地址和實(shí)際物理地址僅僅是相差一個偏移量(PAGE_OFFSET),你可以很方便的將其轉(zhuǎn)化為物理內(nèi)存地址,同時內(nèi)核也提供了virt_to_phys()函數(shù)將內(nèi)核虛擬空間中的物理映射區(qū)地址轉(zhuǎn)化為物理地址。要知道,物理內(nèi)存映射區(qū)中的地址與內(nèi)核頁表是有序?qū)?yīng)的,系統(tǒng)中的每個物理頁面都可以找到它對應(yīng)的內(nèi)核虛擬地址(在物理內(nèi)存映射區(qū)中的)。

而vmalloc分配的地址則限于vmalloc_start與vmalloc_end之間。每一塊vmalloc分配的內(nèi)核虛擬內(nèi)存都對應(yīng)一個vm_struct結(jié)構(gòu)體(可別和vm_area_struct搞混,那可是進(jìn)程虛擬內(nèi)存區(qū)域的結(jié)構(gòu)),不同的內(nèi)核虛擬地址被4k大小的空閑區(qū)間隔,以防止越界——見下圖)。與進(jìn)程虛擬地址的特性一樣,這些虛擬地址與物理內(nèi)存沒有簡單的位移關(guān)系,必須通過內(nèi)核頁表才可轉(zhuǎn)換為物理地址或物理頁。它們有可能尚未被映射,在發(fā)生缺頁時才真正分配物理頁面。

看完上述內(nèi)容,你們對怎么搞懂Linux內(nèi)存管理有進(jìn)一步的了解嗎?如果還想了解更多知識或者相關(guān)內(nèi)容,請關(guān)注億速云行業(yè)資訊頻道,感謝大家的支持。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI