目錄
前言:
一、分頁式存儲管理
二、二級頁表的地址轉化
三、缺頁中斷
總結
前言:
我們上篇文章簡單介紹了線程的一些知識點,但是還有很多坑沒有給大家填上,包括頁表部分我們還沒為大家說明。
本篇文章我將會繼續為大家講解線程的有關內容,希望對大家有所幫助。
一、分頁式存儲管理
我們今天先繼續談論關于分頁式存儲的話題。
首先就是幫助大家深入的了解頁表。
我們之前在學習進程PCB的時候就已經接觸到了頁表,在上文我們也曾提到過:在分頁式存儲管理中,虛擬地址空間被劃分為固定大小的頁(Page)(通常為4KB),而物理內存則被劃分為相同大小的頁框(Page Frame)。操作系統通過頁表(Page Table)建立虛擬頁到物理頁框的映射關系,使得進程可以透明地訪問物理內存。
我們的每一個進程都有自己的頁表結構,這是大家之前就知道的。但是這個頁表結構具體是什么樣的呢?
我們之前只說了頁表負責存儲虛擬地址空間到物理地址的映射關系,但是頁表具體怎么存儲的呢?
在32位系統中,虛擬空間的最大空間是4GB,這是每一個用戶程序都擁有的虛擬內存空間。既然需要讓4GB的虛擬內存全部可用,那么頁表中就需要能夠表示所4GB空間的表項數量,也就是4GB/4KB=1048576個表項,如下圖所示:
-
將原1M個條目的單級頁表拆分為?1024個小頁表(每個小頁表存儲1024個條目,占4KB)。
-
新增一個頁目錄(Page Directory),包含1024個條目,每個條目指向一個小頁表。
-
總容量不變:1024小頁表×1024條目/頁表=1M1024小頁表?×1024條目/頁表?=1M條目,仍可覆蓋4GB空間。
?
?
頁目錄表就是用來負責管理小頁表的結構。所有小頁表的物理地址被頁目錄表項指向,而頁目錄的物理地址被CR3寄存器指向,這個寄存器中,保存了當前正在執行任務的頁目錄地址。
所以操作系統在加載用戶程序時,不僅僅需要為程序內容來分配物理地址,還需要為用來保存程序的頁目錄和頁表分配物理地址哦!!
二、二級頁表的地址轉化
事實上,20位可尋址1M個頁框 × 4KB = 4GB,與32位地址空間上限一致。并且物理頁框的基址必須是4KB的整數倍(即低12位全為0),因此存儲物理地址時無需記錄低12位,硬件會自動在拼接時補零。如0x123
?→ 物理頁框基址 =?0x123000。
所以我們如果把二級頁表的32位全部用來存儲地址,會造成浪費。于是,我們的二級頁表的32位的前20位才是存儲的物理地址,而后面12位,存儲的是頁偏移。
什么意思呢?就是對我們32位的虛擬地址來說,我們可以把這32個數字劃分為三段。
第一段1-10位,表示的是這個虛擬地址代表的頁目錄,2^10次方剛好就是1024個頁目錄項。
第二段11-20位,這個是在找到了頁目錄項的基礎上,找到這個頁目錄項上面的頁表項。
第三段21-32位,表示的是偏移量,我們最后結合這個偏移量找到物理地址:
以0000000000 1111111110 111111111111為例:
?我們32位的物理地址也是差不多的理由,只不過是劃分了兩段。
第一段1-20位表示物理頁框號,第二段21-32位表示標志位,包括讀寫權限這些。
在CPU內部,有一個叫做MMU的硬件電路。以上其實就是MMU的工作流程,他的速度很快,主要工作還是進行內存管理,地址轉化只是他承接的任務之一。
然而還有一個問題,MMU要先進行兩次頁表查詢確定物理地址,在確定了權限等問題之后,MMU再將這個物理地址發送給總線,內存收到之后開始讀取對應的地址的數據并返回。那么當頁表變為N級的時候,就變成了N次檢索加1次讀寫。可見,頁表的級數越多,他所花費的步驟越多,那么對于CPU來說,等待的時間也就越長。
(總線是計算機系統中用于在各部件之間傳輸數據、地址和控制信號的?公共通信通道。它相當于計算機的“神經系統”,負責連接CPU、內存、I/O設備等組件,確保它們能高效協同工作)
所以多級頁表是一個雙刃劍,在減少連續存儲要求且減少存儲空間的同時降低了查詢效率(以時間換取空間)?

三、缺頁中斷
操作系統采用了按需分配的惰性策略,進程的虛擬地址空間雖然理論上覆蓋整個范圍(如32位系統的4GB),但實際僅對當前使用的區域初始化頁表映射。(因為我們之前說過進程運行只需要幾張物理頁就行),如果我們把映射關系全部初始化,會浪費大量內存存儲未使用的頁表項。所以,當我們CPU給MMU的虛擬地址,在TLB與頁表都沒找到對應的物理頁,該怎么辦呢?
這個時候,就想到我們之前說過的延時分配機制,這個我們在講進程的寫實拷貝與動態內存管理申請空間時也提到過一點。比如在動態內存分配(malloc)時,操作系統并不會立即分配實際的物理頁,而是等到程序首次訪問這塊內存時才通過缺頁中斷來真正分配;又如在 fork() 創建子進程時,父子進程共享相同的物理頁,只有當某個進程嘗試寫入時,才會觸發缺頁中斷并執行真正的頁面復制。
寫實拷貝真實是怎么做到的呢?原因是什么呢?
就是因為映射關系在頁表上并未全部給你加載到內存上(雖然所有的映射關系早就初始化了),當程序首次訪問未分配的堆內存、文件映射區域或換出到磁盤的頁面時, MMU 在頁表項中發現"存在位"(Present Bit)為0時,表明該虛擬頁要么尚未關聯物理頁,要么對應的數據還未加載到內存中(可能被換出到交換空間)。這時,作為 CPU 一部分的 MMU 就會觸發一個特殊的軟中斷,將控制權交給操作系統的缺頁處理程序。內核會根據不同的缺頁原因采取相應措施:對于未分配的頁面會分配新的物理頁;對于被換出的頁面會從磁盤換回;對于權限不足的訪問則會拋出段錯誤。處理完成后,操作系統會更新頁表映射,并讓 CPU 重新執行引發缺頁的指令。
這個中斷,就叫做缺頁中斷(缺頁異常)。
正是通過缺頁中斷這個橋梁,現代操作系統才能如此優雅地實現虛擬內存管理,讓每個進程都"錯覺"自己獨占了整個地址空間,而實際上物理內存資源在被所有進程高效共享。
總結
計算機系統的內存管理是一個環環相扣的精妙體系。當進程運行時,它看到的是一個連續的虛擬地址空間(32位系統為4GB),這個設計抽象了物理內存的碎片化問題。但虛擬地址必須轉換為真實的物理地址才能訪問內存,這個轉換過程通過頁表(Page Table)實現,而頁表本身也存儲在物理內存中。
而我們為了高效管理這個轉換過程,采用多級頁表結構(如x86的兩級頁表)。第一級是頁目錄,存儲1024個頁目錄項(PDE),每個PDE指向一個頁表;第二級頁表存儲1024個頁表項,每個PTE最終指向4KB的物理頁框。這種層級結構通過虛擬地址的10-10-12分拆實現:高10位定位頁目錄項,中間10位定位頁表項,低12位作為頁內偏移。
地址轉換時,MMU首先查詢TLB快表(緩存結構),若未命中則需遍歷頁表。為提升效率,操作系統采用按需分配策略:不會為整個4GB空間初始化頁表,而是僅維護當前活躍區域的映射。當訪問未映射的區域時,MMU發現頁表項中"存在位"(Present Bit)為0,觸發缺頁異常。
缺頁處理程序會根據不同情況采取行動:
-
若是首次訪問的堆內存(如malloc分配),則分配物理頁并建立映射
-
若是被換出的頁面,則從交換空間換入
-
若是寫時復制(COW)場景,則復制物理頁并更新映射
這種機制與物理內存管理緊密耦合:操作系統通過伙伴系統管理物理頁框分配,通過頁緩存加速磁盤數據讀取。
希望對大家有所幫助。
明天我們將重新講回線程的知識點,這個知識點時講到線程了順帶引出的,但也很重要!!!?