從前幾篇文章可以看得出來,筆者對於 kernel mm subsystem 其實很不熟悉啊!
決定調整一下整個系列的規劃,在簡介規劃時,接下來要讀的是 cgroup 相關的文件;但看起來,kernel 中像是 mm、中斷相關這些更為基礎的 infrastructure 知之都不甚詳,所以接下來還是先針對記憶體管理相關的文件好好的閱讀;
接下來,會來閱讀 Linux Memory Management Documentation 這裡面的文件並做紀錄,而筆者對於開機過程中,實體記憶體是如何被記錄和操作,分頁表是如何建立的尤有興趣,希望有機會能在這些文件找到一些蛛絲馬跡,再搭配 RTFSC 大法,做一個完整的紀錄。
事不宜遲,今天就從 Physical Memory Model 開始吧!
.. SPDX-License-Identifier: GPL-2.0
.. _physical_memory_model:
=============
實體記憶體模型
=============
系統中的實體記憶體可以用不同的方式定址。
最簡單的情況是實體記憶體從位址 0 開始並延續一個連續的範圍直到最大位址。
然而,這範圍可能包含 CPU 無法存取的記憶體"孔洞";並且有可能存在不同的連續範圍在不同的位址。
還有別忘記 NUMA,其中不同的記憶體組連接到不同的 CPU 上。
Linux 使用兩種記憶體模型來抽像這種多樣性:FLATMEM 和 SPARSEMEM。
每個架構都定義了它支援的記憶體模型、預設的記憶體模型是什麼以及是否可以手動覆蓋該預設值。
所有記憶體模型都使用一個或多個陣列中的 struct page 來紀錄實體頁框的狀態。
無論選擇哪種記憶體模型,實體頁框號碼(PFN)和相應的 struct page 之間都存在一對一的映射關係。
每個記憶體模型都定義了 :c:func:`pfn_to_page` 和 :c:func:`page_to_pfn`,
這兩個 helper 實作了 PFN 以及 struct page 之間的轉換。
FLATMEM
=======
最簡單的記憶體模型是 FLATMEM。該模型適用於具有連續,
或大部分連續實體記憶體的非 NUMA 系統。
在 FLATMEM 記憶體模型中,有一個全域的 `mem_map` 陣列映射了整個實體記憶體。
對於大多數架構,"記憶體孔洞" 是會占用 `mem_map` 陣列中元素;
而若 `struct page` 物件對應的是"記憶體孔洞",則該物件不會被完全的初始化。
要分配記憶體給 `mem_map` 陣列,架構相關的設置程式碼應該使用 :c:func:`free_area_init` 函數。
然而,直到呼叫 :c:func:`memblock_free_all` 來將所有記憶體轉交給分頁分配器(page allocator)之前,
這個映射陣列(mapping array) 是不可以使用的。
某些架構可能會釋放某部分並未對應到實際實體記憶體分頁的 `mem_map` 陣列,
在這種情況下,架構相關的 :c:func:`pfn_valid` 實做需要將在 `mem_map` 中的記憶體孔洞考慮在內。
在 FLATMEM 中,PFN 和 `struct page` 之間的轉換是非常直覺的:
`PFN - ARCH_PFN_OFFSET` 是 `mem_map` 陣列的索引 (index)。
`ARCH_PFN_OFFSET` 定義了實體記憶體的起始位址不為 0 系統的第一個頁框編號。
FLATMEM
就是一般記憶體的感覺;而 SPARSEMEM
感覺上像是記憶體上有很多的"孔洞" (無法存取或是留給其他裝置的記憶體區段),不過這邊我們等文件都看完了再來詳細記錄struct page
來記錄實體記憶體分頁,而一般分頁大小是 4KBstruct page
和實體記憶體分頁編號 (PFN) 可以透過 pfn_to_page
、page_to_pfn
來做轉換今天先翻譯了這篇文件的前三分之一,文件本身的說明都滿直觀、簡潔的,有一些基礎的 paging 概念就能大致上稍微理解目前看到的文件內容,至於文件中提到的 API 細節,我們就留待後續揭曉囉!
感謝各位,我們明天見!