CPU如何知道哪個物理地址映射到哪個虛擬地址?
根據我的理解,每個程序都是通過虛擬地址而不是物理地址來訪問記憶體的,通過MMU單元將這些虛擬地址轉換為物理地址是CPU的職責,兩個或多個程序可以擁有相同的虛擬地址。
因此,假設程序 A正在嘗試訪問虛擬地址
12345
,並且程序 B正在嘗試訪問虛擬地址12345
。MMU是如何將每個程序的虛擬地址翻譯成物理地址的,它是否有每個程序的映射表,將虛擬地址映射到物理地址(因為我以為CPU甚至都不知道“程序”是什麼,而它唯一的職責就是盲目地執行指令而不關心哪條指令屬於哪個程序,而“程序”只是一個OS概念)?
在 Linux 中,核心維護一個五級頁表(不管 CPU 的能力如何;多餘的級別在編譯時被去除)。最頂層是頁面全域目錄,每個程序都有自己的目錄,
pgd
在mm_struct
. 因此每個程序都可以有自己的映射,所以不同程序中的地址12345指向不同的物理地址。CPU 並不真正了解程序,但它們確實傾向於具有支持它們的功能。在 x86 風格的 CPU 上,有各種與任務相關的功能,但它們實際上往往被忽略。由於程序調度是由核心管理的,它可以自己跟踪頁表更改,並在切換任務時更新切換到新程序頁表所需的任何 CPU 狀態。在 x86 PC 上,這涉及更新指向頁面目錄的CR3控制寄存器。
Mel Gorman 的Understanding the Linux Virtual Memory Manager一書中的頁表管理一章給出了很好的概述。
MMU訪問描述如何將虛擬地址轉換為物理地址的表。(它不需要將物理地址轉換為虛擬地址,這通常是不可能的,因為可以通過多個虛擬地址訪問相同的物理地址,也可以取消映射。)該表的佈局取決於 CPU 架構,但一般原則始終是相同的:有一個 CPU 寄存器包含表的物理地址,其中包含更多表的物理地址,依此類推(在現有架構上總共有 2 到 4 級),直到表的級別包含數據所在的物理地址。在每一層,使用表中的哪個元素是由虛擬地址中的一些位決定的。
MMU 不知道作業系統程序本身。當 CPU 切換到執行不同的程序時,即發生上下文切換時,作業系統的上下文切換程式碼的工作是根據需要更新 MMU 表。在實踐中,我認為所有 Unix 系統都會在記憶體中為每個程序保留一份表的副本,並且只需更新 MMU 寄存器以指向目前程序的頂級表。
實際上,MMU 有一部分關心作業系統程序:TLB. 在 MMU 表中查找條目的成本相當高,因為它涉及多次記憶體訪問。TLB 是這些查找的記憶體。在上下文切換時,作業系統必須使 TLB 無效(即刪除所有記憶體條目),因為新程序的映射將不同。許多架構允許作業系統在每個 MMU 表條目中放置一個指示符來表示“該條目屬於程序 N”。如果 TLB 條目包含的程序號不是目前程序號,則跳過它。CPU 寄存器包含目前程序號,上下文切換程式碼會更新它。這種機制意味著 TLB 可以同時包含有關多個程序的資訊,從而提高了在這些程序之間來回切換時的性能。