嵌入式基礎知識--存儲管理之分頁管理
時間:2023-10-19 來源:華清遠見
現代計算機之父馮諾伊曼最先提出程序存儲的思想,并成功將其運用在計算機的設計之中,該思想約定了用二進制進行計算和存儲,還定義計算機基本結構為 5 個部分,分別是中央處理器(CPU)、內存、輸入設備、輸出設備、總線。

存儲器:代碼跟數據在RAM跟ROM中是線性存儲, 數據存儲的單位是一個二進制位。最小的存儲單位是字節。
總線:總線是用于 CPU 和內存以及其他設備之間的通信,總線主要有三種:
地址總線:用于指定 CPU 將要操作的內存地址。
數據總線:用于讀寫內存的數據。
控制總線:用于發送和接收信號,比如中斷、設備復位等信號,CPU 收到信 號后響應,這時也需要控制總線。
輸入/輸出設備:輸入設備向計算機輸入數據,計算機經過計算后,把數據輸出給輸出設備。比如鍵盤按鍵時需要和 CPU 進行交互,這時就需要用到控制總線。
CPU:中央處理器,類比人腦,作為計算機系統的運算和控制核心,是信息處理、程序運行的最終執行單元。CPU用寄存器存儲計算時所需數據,寄存器一般有三種:
通用寄存器:用來存放需要進行運算的數據,比如需進行加法運算的兩個數據。
程序計數器:用來存儲 CPU 要執行下一條指令所在的內存地址。
指令寄存器:用來存放程序計數器指向的指令本身。
在馮諾伊曼體系下電腦指令執行的過程:
CPU讀取程序計數器獲得指令內存地址,CPU控制單元操作地址總線從內存地址拿到數據,數據通過數據總線到達CPU被存入指令寄存器。
CPU分析指令寄存器中的指令,如果是計算類型的指令交給邏輯運算單元,如果是存儲類型的指令交給控制單元執行。
CPU 執行完指令后程序計數器的值通過自增指向下個指令,比如32位CPU會自增4,循環往復。
有了馮諾伊曼計算機體系后,電腦想要為用戶提供便捷的服務還需要安裝個操作系統Operation System,操作系統是覆蓋在硬件上的一層特殊軟件,它管理計算機的硬件和軟件資源,為其他應用程序提供大量服務。可以理解為操作系統是日常應用程序跟硬件之間的接口。日常你經常在用Windows/Linux 系統,操作系統給我們提供了超級大的便利,但是你了解操作系統么?操作系統是如何進行內存管理、進程管理文件管理、網絡管理、設備管理的呢?接下來我們主要研究一下關于內存管理的相關知識中的內存分頁管理。
我們的電腦是32位操作系統,那可支持的最大內存就是4G,你有沒有好奇為什么可以同時運行2個以上的2G內存的程序。應用程序不是直接使用的物理地址,操作系統為每個運行的進程分配了一套虛擬地址,每個進程都有自己的虛擬內存地址,進程是無法直接進行物理內存地址的訪問的。至于虛擬地址跟物理地址的映射,進程是感知不到的!操作系統自身會提供一套機制將不同進程的虛擬地址和不同內存的物理地址進行映射。

MMU:
Memory Management Unit 內存管理單元是一種負責處理CPU內存訪問請求的計算機硬件。它的功能包括虛擬地址到物理地址的轉換、內存保護、中央處理器高速緩存的控制。現代 CPU 基本上都選擇了使用 MMU。當進程持有虛擬內存地址的時候,CPU執行該進程時會操作虛擬內存,而MMU會自動的將虛擬內存的操作映射到物理內存上。
內存頁管理方式
內存分頁,整個虛擬內存和物理內存切成固定尺寸大小段。每個固定大小的段尺寸稱之為頁Page,在 Linux 系統中Page = 4KB。然后虛擬內存跟物理內存之間通過頁表來實現映射。采用內存分頁時內存的釋放跟使用都是以頁為單位的,也就不會產生內存碎片了。當空間不夠時根據操作系統調度算法,可將最少用的內存頁面 swap-out換出到磁盤,用時候再swap-in換入,盡可能的減少磁盤刷寫量,提高內存交互效率。分頁模式下虛擬地址主要由頁號跟頁內偏移量兩部分組成。通過頁號查詢頁表找到物理內存地址,然后再配合頁內偏移量就找到了真正的物理內存地址。

使用 4K 字節固定大小的頁面,每個頁面均是 4KB,并且對其于 4K 地址邊界處。 這表示分頁機制把 2^32字節(4GB)的線性地址空間劃分成 2^20(1M = 1048576)個頁面。分頁機制通過把線性地址空間中的頁面重新定位到物理地址空間中進行操作。由于 4K 大小的頁面作為一個單元進行映射,并且對其于 4K 邊界,因此線性地址的低 12 位可做為頁內偏移地量直接作為物理地址的低 12 位。分頁機制執行的重定向功能可以看作是把線性地址的高 20 位轉換到對應物理地址的高 20 位。
32位操作系統環境下進程可操作的虛擬地址是4GB,假設一個虛擬頁大小為4KB,那需要4GB/4KB = 2^20 個頁信息。一行頁表記錄為4字節,2^20等價于4MB頁表存儲信息。這只是一個進程需要的,如果10個、100個、1000個呢?僅僅是頁表存儲都占據超大內存了。為了解決這個問題就需要用到 多級頁表,核心思想就是局部性分配。在32位的操作系統中將將4G空間分為 1024 行頁目錄項目(4KB),每個頁目錄項又對應1024行頁表項。

32位系統二級分頁
控制寄存器cr3中存放了頁目錄的物理地址,通過cr3寄存器可以找到頁目錄,而32位線性地址中的Directory部分決定頁目錄中的目錄項,而頁目錄項中存放了要找的頁表的物理基地址,再結合線性地址中的中間10位頁表項,就可以找到頁框的頁表項。線性地址中的Offset部分占12位,因此頁框的物理地址 + 線性地址Offset部分 = 頁框中的任何一個字節。
分頁后一級頁就等價于4G虛擬地址空間,并且如果一級頁表中那些地址沒有就不需要再創建二級頁表了!核心思想就是按需創建,當系統給每個進程分配4G空間,進程不可能占據全部內存的,如果一級目錄頁只有10%用到了,此時頁表空間 = 一級頁表4KB + 0.1 * 4MB 。這比單獨的每個進程占據4M好用多了!
多層分頁的弊端就是訪問時間的增加
使用頁表時讀取內存中一頁內容需要2次訪問內存,訪問頁表項 + 并讀取的一頁數據。
使用二級頁表的話需要三次訪問,訪問頁目錄項 + 訪問頁表項 + 訪問并讀取的一頁數據。訪存次數的增加也就意味著訪問數據所花費的總時間增加。
而對于64位系統,二級分頁就無法滿足了,Linux 從2.6.11開始采用四級分頁模型。
Page Global Directory 全局頁目錄項
Page Upper Directory 上層頁目錄項
Page Middle Directory 中間頁目錄項
Page Table Entry 頁表項
Offset 偏移量。

TLB
Translation Lookaside Buffer 可翻譯為地址轉換后援緩沖器,簡稱為快表,屬于CPU內部的一個模塊,TLB是MMU的一部分,實質是cache,它所緩存的是最近使用的數據的頁表項(虛擬地址到物理地址的映射)。他的出現是為了加快訪問數據(內存)的速度,減少重復的頁表查找。當然它不是必須要有的,但有它,速度就更快。

TLB很小,因此緩存的東西也不多。主要緩存最近使用的數據的數據映射。TLB結構如下圖:

TLB查詢
如果一個需要訪問內存中的一個數據,給定這個數據的虛擬地址,查詢TLB,發現有hit,直接得到物理地址,在內存根據物理地址取數據。如果TLB沒有這個虛擬地址miss,那么只能費力地通過頁表來查找了。日常CPU讀取一個數據的流程如下:

讀取數據流程圖
當進程地址空間進行了上下文切換時,比如現在是進程1運行,TLB中放的是進程1的相關數據的地址,突然切換到進程2,TLB中原有的數據不是進程2相關的,此時TLB刷新數據有兩種辦法。
全部刷新:很簡單,但花銷大,很多不必刷新的數據也進行刷新,增加了無畏的花銷。
部分刷新:根據標志位,刷新需要刷新的數據,保留不需要刷新的數據。
以上就是關于內存分頁管理的簡單的分析,當然,內存管理還有分段管理,段頁式管理等等,我們以后再分析。

