Linux操作系統的靈魂,深度解析MMU內存管理
在計算機的奇妙世界里,我們每天使用的操作系統看似流暢自如地運行著各類程序,背后實則有著一位默默耕耘的 “幕后英雄”—— 內存管理單元(MMU)。它雖不常被大眾所熟知,卻掌控著計算機內存的關鍵命脈,是保障系統穩定、高效運行的核心力量,宛如一場精彩演出背后的總導演,掌控著舞臺上的每一個細節。
當我們打開電腦,同時運行多個程序時,有沒有想過它們是如何在有限的內存空間里 “和平共處” 的?又或者,當程序所需內存遠超實際物理內存時,為何系統依然能有條不紊地運行?這一切,都離不開 MMU 的神奇魔法。接下來,就讓我們一起揭開 MMU 的神秘面紗,探尋它在操作系統中的精彩故事。
一、引言
虛擬內存是現代操作系統中最偉大的發明之一。它為每個進程提供了一個一致的、私有的地址空間,讓每個進程產生了一種自己在獨享主存的錯覺。
為了講清楚MMU是如何一步一步完成地址翻譯,取出數據的,本篇文章在前部分中講解了虛擬內存中一些重要的概念,比如,虛擬內存的作用,頁命中,缺頁異常處理,為什么需要TLB等等。最后,通過兩個地址翻譯的例子,詳細解釋了MMU地址翻譯的過程。
⑴什么是虛擬內存?
- 虛擬內存能夠創建一個連續的更大的空間給進程使用,出現的原因是由于主存的空間是有限。
- 當運行多個進程或者一個進程需要更大的空間進行存儲運行,主存顯然是不夠的,這個時候就需要更大更便宜的磁盤進行保存一部分數據。
- 對于進程來說,虛擬內存就是一張連續的內存空間,這個空間有些在主存中,有些在磁盤中。
⑵虛擬內存的作用
- 虛擬內存將主存看成是一個存儲在磁盤上的地址空間的高速緩存,在主存中只保存活動區域,并根據需要在磁盤和主存之間來回傳送數據,通過這種方式,可以高效地使用主存。
- 虛擬內存為每個進程提供了一致的地址空間,簡化了內存管理。
- 虛擬內存保護了每個進程的地址空間不被其他進程破壞。
⑶虛擬內存與物理內存
圖片
我們先來看下,CPU是如何根據地址取得數據的。CPU 在這里生成的物理地址為 4,把地址發送給內存,然后內存從該地址獲取其中保存的字,最后將其發送回 CPU;MMU(Memory Management Unit)叫做內存管理單元,主要用來管理虛擬內存與物理內存的映射,由硬件自動完成。
⑷頁命中/缺頁
圖片
- 處理器產生一個虛擬地址。
- MMU生成PTE地址,并從高速緩存/主存請求得到它。
- 高速緩存/主存向MMU返回PTE。
- MMU構造物理地址,并把它傳送給高速緩存/主存。
- 高速緩存/主存返回所請求的數據字給處理器。
圖片
- 處理器產生一個虛擬地址。
- MMU生成PTE地址,并從高速緩存/主存請求得到它。
- 高速緩存/主存向MMU返回PTE。
- PTE中的有效位是零,所以MMU觸發了一次異常,傳遞CPU中的控制到操作系統內核中的缺頁異常處理程序。
- 缺頁處理程序確定出物理內存中的犧牲頁,如果這個頁面已經被修改了,則把它換出到磁盤。
- 缺頁處理程序頁面調入新的頁面,并更新內存中的PTE。
- 缺頁處理程序返回到原來的進程,再次執行導致缺頁的指令。CPU將引起缺頁的虛擬地址重新發送給MMU。因為虛擬頁面現在緩存在物理內存中,所以就會命中,主存就會將所請求字返回給處理器。
⑸為什么有了高速緩存,還需要TLB呢?
局部性原則保證了在任意時刻, 程序將往往在一個較小的活動頁面集合上工作,這個集合叫做工作集或者常駐集。換句話說, 局部性原則揭示了一個現象:在一段時間內,我們會反復調入或調出同一個或幾個虛擬頁頁面。而且,每次CPU產生一個VA時, MMU就必須查閱PTE,以便將VA翻譯為PA, 注意是每次,所以開銷很大。
解決方法: 為了消除這樣的開銷,在MMU中包括了一個關于PTE的小緩存,稱為翻譯后備緩沖器,TLB(Translation Lookaside Buffer)。
關鍵點: 所有的地址翻譯步驟都是在芯片上的MMU中執行的, 因此執行速度非常快。
二、主角登場:MMU是何方神圣
MMU是Memory Management Unit的縮寫,中文名是內存管理單元,有時稱作分頁內存管理單元(英語:paged memory management unit,縮寫為PMMU)。它是一種負責處理中央處理器(CPU)的內存訪問請求的計算機硬件。它的功能包括虛擬地址到物理地址的轉換(即虛擬內存管理)、內存保護、中央處理器高速緩存的控制,在較為簡單的計算機體系結構中,負責總線的仲裁以及存儲體切換(bank switching,尤其是在8位的系統上)。
內存管理單元(MMU)的一個重要功能是使系統能夠運行多個任務,作為獨立程序在自己的私有虛擬內存空間中運行。它們不需要了解系統的物理內存映射,即硬件實際使用的地址,也不需要了解可能同時執行的其他程序。
具體而言,MMU 肩負著兩大關鍵職責。其一,負責虛擬地址與物理地址的映射,這意味著它能將程序使用的虛擬地址巧妙地轉換為實際的物理內存地址,讓每個進程都仿佛擁有了屬于自己的獨立內存空間,彼此互不干擾;其二,它還提供硬件機制的內存訪問授權,嚴格把關每一次內存訪問,確保進程只能在允許的范圍內讀寫內存,有效防止非法訪問,維護系統的穩定性與安全性。
圖片
2.1MMU的由來
⑴Swap模式時代
早期計算機在執行程序時,將程序從磁盤加載到內存執行中執行,在多用戶系統中,當新的用戶程序被執行前,需要先將當前用戶的程序從內存swap到磁盤,然后從磁盤加載新的程序執行,當前用戶退出后,在將前一用戶程序從磁盤中加載到內存繼續執行,每次用戶切換伴隨程序的swap,消耗較大。
⑵Page模式時代
后來人們將內存劃分為固定大小的Page,一般為4K或者更小,這樣用戶程序按需以Page的方式加載到內存,不需要將整個程序加載到內存,這樣內存可同時容納更多程序,而無需按照用戶切換進行swap,提升了內存利用率和加載時間(PageFault是Page時代的產物,而非MMU時代獨有)。
⑶動態地址轉換(DAT - Dynamic Address Translation)
最早可以追溯到1966年IBM研發的System/360-Model67,在該計算機的設計中首先引入了動態的地址轉換機制,在Page模式基礎上,為用戶程序分配虛地址(VA),通過DAT轉換為物理地址(PA)進行訪問。通過好處是用戶可以使用連續的地址,而不再受制于物理內存大小和Page碎片化的限制,原則上用戶的程序只受磁盤大小限制,代價是增加虛實地址轉換機制。
①實現方式
采用segment、page、offset模式,將24-bit虛擬地址分為3段,0-7bit保留,8-11bit索引segment,一共16個segment,12-19bit索引page,每個segment最多256個page,20-31bit為page offset,每個用戶有一個虛實地址映射表,分為2級,即segment表和page表,每個segment指向一個page表。
②轉換過程
當CPU訪問一個虛地址時,先通過VA的8-11bit查找segment表得到page表,再根據12-19bit在page表中找到PA的page起始地址,加上20-31bit的page offset就得到實際的PA了。
這就是MMU最早的雛形了。
2.2現代MMU
實地址模式:即CPU狀態位中MMU使能位清零,MMU處于關閉狀態,此時CPU操作的地址不經過轉換(VA=PA),直接作為物理地址進行訪問,CPU上電時或者在異常入口時處于該狀態,在該狀態下可以訪問任意物理內存,非常危險,一般操作系統在CPU上電后做完必要初始化以后便使能MMU,或者在異常處理的入口保存好必要信息后使能MMU。
塊地址轉換:或者成為固定的地址轉換或靜態配置的地址轉換表,這種模式支持配置一些固定的內存地址映射(VPN->RPN),比如Linux Kernel加載的地址,以PowerPC604為例,0xC0000000這段地址開始的256M內存映射使用了該模式的轉換,好處是這種配置轉換速度快,一般在特定的寄存器中配置,沒有頁表查找過程,缺點是缺乏靈活性,一次配置永久使用。
頁地址轉換:類似于早期的動態地址轉換DAT,即將VA的一部分bit用于索引segment,另外一部分bit用于索引PTE表,最終得到物理地址的Page起始地址,再加上最后12bit(4K)的Page offset得到真正的物理地址。
相比早期的DAT,有以下優化:
增加了PTE表的緩存TLB(Translation lookaside Buffer),有些處理器將ITLB(指令)和DTLB(數據)分開,以減少指令和數據之間的緩存沖突。
支持更大的物理地址(36bit以上)或邏輯地址,如在PowerPC中,用以應對現代操作系統的多進程管理將32bit通過segment寄存器擴展為52bit的邏輯地址,然后通過hash函數得到key用來查找PTE,并最終轉換為36bit物理地址,邏輯地址的擴展用于減少多進程之間的PTE沖突。
支持多種PTE查找方式,如硬件查找和軟件查找。現在CPU的MMU地址轉換流程如下:
圖片
主要分為幾個階段:
- 用戶進程訪問虛存地址。
- 觸發TLB查找過程,該部分通過硬件完成(灰色背景),沒有軟件參與。
- TLB miss場景下,查找PTE(粉色背景),該部分在不同CPU上實現不同,像X86都是硬件查找,PowerPC有些處理器使用軟件查找,即在內核實現一個TLB miss的異常處理,可以靈活做到TLB查找。
Do Page Fault,分為幾種情況:
- 新申請內存第一次讀寫,觸發物理內存分配
- 進程fork后子進程寫內存觸發Copy-On-Write。
- 非法內存讀寫,錯誤處理。
MMU在CPU的配合下(通過頁異常觸發),實現了線性地址到物理地址的動態映射,為正在CPU上運行的應用程序(進程)提供了一個獨立的連續內存空間(線性地址空間,或稱虛擬內存空間,其中放置了代碼段、數據段和堆棧段),屏蔽了地址分配、內存分配和內存回收等一系列復雜的系統行為。
MMU的線性地址轉換是通過頁表進行的,具體過程如下圖所示:
圖片
其實最簡單明了的方法是通過一個一維數組來記錄映射關系:下標代表線性地址,數組元素內容代表物理地址。可是如此一來,用來表示映射關系的內存空間比被表示的物理空間還要大,顯然這不是一個可行的方案。
工程師們采用了分段分級的思路來表示這種映射關系:先把線性空間以4K大小為單位進行劃分(頁),然后再以大段連續空間進行轉換,在每個大段空間內部再次劃分成小段進行轉換,直到段大小變為4K頁大小。用以表示和段空間映射關系的結構稱為頁表,其大小也是一個頁面。由于采用了分段的方法,頁表空間大大減小;同時未映射的空間不必分配頁表,這也進一步降低了頁表占用空間。
x86_64架構下Linux用了四級頁表來表示一個映射關系,依次為PGD、PUD、PMD、PT。每級頁表4K大小,內部元素大小為8字節,高位指向了下一級頁表的物理地址,低位表示頁表屬性(是否存在、讀寫權限、是否臟等等)。頂層頁表PGD的物理地址存放在CPU的CR3寄存器中,供MMU訪問。48位線性地址也相應地分成了五段:前四段,每段長9位,用來索引對應頁表的元素;最后一段長12位,用來在頁面中索引物理地址。
三、MMU的工作原理
3.1虛擬地址到物理地址的奇幻之旅
在計算機的世界里,每個程序都像是一個擁有獨立王國的小世界,它們運行在自己的虛擬地址空間中。這個虛擬地址空間的大小,取決于計算機 CPU 的位數。例如,對于常見的 32 位 CPU,其虛擬地址空間范圍是 0 到 2^32 - 1,也就是 4GB;而 64 位 CPU 的虛擬地址空間更是高達 2^64 - 1,這是一個極其龐大的數字,理論上能提供 16EB(1EB = 1024PB,1PB = 1024TB)的尋址能力。
然而,現實中的物理內存可沒有這么夸張。一臺普通的家用電腦,物理內存可能只有 8GB、16GB 甚至更小。這就好比,虛擬地址空間是一片廣闊無垠的虛擬大陸,而物理內存只是這片大陸上實實在在存在的一小片陸地。那么,如何讓程序在這片虛擬大陸上暢行無阻,又能精準地找到物理內存中的對應位置呢?這就輪到 MMU 大展身手了。
當程序運行并試圖訪問內存時,它使用的是虛擬地址。這個虛擬地址就像是一張神秘的地圖,指引著程序前往它想去的地方。MMU 拿到這個虛擬地址后,會依據預先設置好的頁表,進行一場精妙絕倫的 “翻譯” 工作,將虛擬地址轉換為物理地址。
這里就涉及到分頁機制這個關鍵概念。分頁,簡單來說,就是把虛擬地址空間和物理地址空間都劃分成一個個大小相等的小塊,虛擬空間中的叫頁(Page),物理空間中的叫頁幀(Page Frame),頁和頁幀就如同拼圖中的小塊,大小必須完美匹配。比如說,在某個系統中,頁和頁幀的大小被設定為 4KB。當程序給出一個虛擬地址,比如 0x12345678,MMU 會迅速將這個虛擬地址拆分成兩部分:虛擬頁面號(VPN)和虛擬地址偏移(VA offset)。同樣,物理地址也由對應的物理頁幀號(PFN)和物理地址偏移(PA offset)組成。
MMU 轉換地址的核心秘訣就在于找到 VPN 與 PFN 之間的對應關系,而這個關系,正靜靜地躺在頁表之中。頁表就像是一本神秘的魔法詞典,存儲著虛擬地址到物理地址的映射關系。MMU 根據 VPN,在頁表中快速查找,找到對應的 PFN,再把 PFN 和 VA offset 相加,剎那間,物理地址就誕生了,程序也順利找到了它夢寐以求的數據。舉個例子,假設程序中的一個變量存儲在虛擬地址 0x00401000,通過 MMU 的轉換,發現它對應的物理頁幀號是 0x0020,偏移量不變,最終得到物理地址 0x00201000,數據就這樣被精準定位。
值得注意的是,頁表本身通常存儲在物理內存中,這就好像把尋寶的線索藏在了寶藏庫里,每次 MMU 轉換地址都得去物理內存里查找頁表,會不會很慢呢?別擔心,工程師們早就想到了辦法 —— 引入了快表(TLB,Translation Lookaside Buffer)。TLB 就像是 MMU 的 “私人小助手”,它是一個高速緩存,專門用來存儲最近使用過的頁表項。MMU 查找地址時,會先在 TLB 中快速搜索,如果找到了,就能瞬間完成地址轉換,大大提高了效率;要是 TLB 中沒找到,才會去物理內存中查找頁表,雖然慢點,但也能確保準確無誤地找到目標。
3.2內存保護的安全護盾
在如今的多用戶多進程操作系統環境下,計算機就像一個熱鬧非凡的大集市,眾多進程熙熙攘攘地同時運行著。每個進程都懷揣著自己的 “小心思”,處理著不同的數據,執行著各異的任務。要是沒有嚴格的規則約束,它們在內存這個 “公共資源區” 里橫沖直撞,必然會亂成一鍋粥,數據混淆、程序崩潰等問題將接踵而至。
而 MMU,就是這個大集市的公正 “管理員”,為每個進程精心打造了獨立的地址空間,讓它們 “井水不犯河水”。比如說,進程 A 和進程 B 都在運行,它們可能都用到了虛擬地址 0x1000,但在 MMU 的巧妙管理下,這兩個虛擬地址會被分別映射到不同的物理內存區域,進程 A 訪問 0x1000 時,得到的是自己的數據,進程 B 訪問時,同樣如此,彼此互不干擾,仿佛每個進程都獨占了一片內存天地。
不僅如此,MMU 還是一位嚴謹的 “安全衛士”,為內存的每一頁都設置了精細的訪問權限。這些權限就像是一道道堅固的門禁,嚴格限制著進程對內存的訪問行為。頁表項中的權限位詳細規定了每頁的訪問規則,常見的權限有可讀(Read)、可寫(Write)、可執行(Execute)等。對于操作系統內核所在的內存頁,為了防止用戶進程誤操作或惡意篡改,通常只設置為可讀和可執行,禁止寫入;而對于普通數據存儲的內存頁,可能允許進程讀寫,但禁止執行,避免代碼注入攻擊。
當進程試圖訪問某一內存頁時,MMU 會第一時間檢查該進程是否具備相應的權限。如果進程違反了權限規則,比如試圖寫入只讀內存頁,MMU 會立刻發出警報,拋出異常,并通知操作系統前來處理。操作系統就會像一位嚴厲的裁判,根據具體情況,可能采取終止違規進程、記錄錯誤信息等措施,確保系統的整體穩定與安全。在一些復雜的服務器系統中,這種內存保護機制尤為重要,它保障著多個用戶的任務能夠安全、穩定地并行運行,互不干擾,為整個計算機系統的平穩運行保駕護航。
四、MMU與不同內存管理方式
在現代操作系統中,內存管理方式多種多樣,而 MMU 在其中扮演的角色也因管理方式的不同而各具特色。
頁式管理,是當下最為常見的內存管理方式之一。它把進程的虛擬地址空間等分成固定大小的頁,物理內存也同樣劃分成與頁大小一致的頁幀。MMU 在此過程中,就像是一位精準的導航員,依據頁表這一 “航海圖”,引導虛擬地址找到對應的物理頁幀。頁表記錄著每頁的映射關系,MMU 通過查找頁表,迅速完成虛擬地址到物理地址的轉換。
這種方式的優勢顯而易見,它極大地提高了內存的利用率,有效減少了外部碎片的產生,使得內存空間的分配更加規整、高效。例如,在一些大型數據庫系統中,需要頻繁地加載和卸載大量數據,頁式管理能夠確保數據在內存中的快速存儲與讀取,保障系統的高效運行。
段式管理,則側重于按照程序的邏輯結構進行分段,每個段都有明確的意義,如代碼段、數據段、堆棧段等。MMU 在段式管理里,負責將段式虛擬地址轉換為物理地址。它借助段表來查找段的基地址,再結合段內偏移量,就能精準定位到物理內存位置。這種管理方式對于程序員而言更加友好,因為它貼合了程序的邏輯結構,方便程序的編寫、編譯與調試。以一個復雜的圖形處理軟件為例,不同功能模塊的代碼和數據可以分別存放在不同的段中,程序員能夠清晰地管理各個模塊,若某個模塊出現問題,也能迅速定位排查,提高開發效率。
段頁式管理,顧名思義,是將段式和頁式管理的優點巧妙融合。它先把程序按邏輯分段,然后每段再進一步分頁。在這種模式下,MMU 的工作變得更為復雜卻也更加精細。系統需要為每個進程建立段表,段表項指向對應的頁表,頁表再負責具體的頁到頁幀的映射。當進程訪問內存時,MMU 先根據段號在段表中找到頁表的起始地址,再依據頁號在頁表中查找物理頁幀號,最終確定物理地址。雖說段頁式管理在地址轉換時,可能需要多次訪問內存,導致一定的性能開銷,但它既具備段式管理的邏輯清晰、便于共享與保護的優點,又擁有頁式管理內存利用率高的長處,適用于那些對內存管理要求極高、既需邏輯結構嚴謹又要高效利用內存的復雜系統,如大型服務器操作系統、高端工業控制系統等。
五、MMU在常見操作系統中的表現
在 Windows 操作系統中,MMU 為系統的穩定運行和多任務處理提供了堅實保障。當我們打開多個辦公軟件、瀏覽器窗口以及各類后臺程序時,MMU 發揮著關鍵作用。它巧妙地將每個進程的虛擬地址空間轉換為物理地址,確保進程間的內存互不干擾。例如,在 32 位的 Windows 系統中,每個進程都擁有獨立的 4GB 虛擬地址空間,MMU 通過精細的頁表管理,讓這些進程能夠在物理內存中和諧共存。同時,Windows 系統借助 MMU 的內存保護功能,嚴格限制進程對系統關鍵內存區域的訪問,有效防止惡意軟件或錯誤程序對系統造成破壞,保障了系統的安全性與穩定性,為用戶提供流暢、可靠的使用體驗。
Linux 作為一款廣泛應用于服務器、嵌入式設備等多種領域的開源操作系統,MMU 的作用同樣舉足輕重。在服務器環境中,面對眾多用戶同時發起的復雜任務請求,Linux 依靠 MMU 實現高效的內存管理。它采用靈活的內存管理策略,如按需分頁,結合 MMU 的地址轉換機制,確保只有當前正在使用的內存頁面被加載到物理內存中,大大提高了內存利用率。同時,對于實時性要求較高的嵌入式 Linux 系統,MMU 能夠精準地為各個任務分配內存資源,保障任務的及時響應與執行,滿足不同場景下的應用需求,充分展現了其強大的適應性與可靠性。
蘋果公司的 macOS 操作系統,憑借其簡潔易用的界面和出色的性能深受用戶喜愛,而這背后離不開 MMU 的默默支持。macOS 在圖形處理、多媒體編輯等高強度任務方面表現卓越,MMU 在其中功不可沒。當運行大型圖形設計軟件或視頻編輯工具時,這些應用程序需要大量的內存空間來存儲圖像、視頻數據以及中間處理結果。MMU 高效地管理內存,快速進行虛擬地址到物理地址的轉換,確保數據的快速讀寫,讓軟件運行流暢,避免卡頓現象,為創意工作者提供了穩定且高效的創作環境,助力他們盡情發揮才華。