1. 基礎資料結構

1.1 LIST_ENTRY 雙向鏈結串列

LIST_ENTRY 是 EDK2 中最基礎的資料結構,用於實現雙向鏈結串列。 它被嵌入到其他結構體中,用於將多個結構體串連起來。

typedef struct _LIST_ENTRY {
    struct _LIST_ENTRY  *ForwardLink;  // 指向下一個節點
    struct _LIST_ENTRY  *BackLink;     // 指向前一個節點
} LIST_ENTRY;

LIST_ENTRY 結構圖解

LIST_ENTRY ForwardLink BackLink LIST_ENTRY ForwardLink BackLink LIST_ENTRY ForwardLink BackLink
ForwardLink BackLink

1.2 MEMORY_MAP_ENTRY 記憶體描述結構

MEMORY_MAP_ENTRY 描述一塊連續的記憶體區域,包含其類型、起始位址、頁數等資訊。 多個 MEMORY_MAP_ENTRY 透過 LIST_ENTRY 串連成記憶體映射表。

typedef struct {
    UINT32          Signature;        // 簽名: 'mmap'
    LIST_ENTRY      Link;             // 串連用的雙向鏈結
    EFI_MEMORY_TYPE Type;             // 記憶體類型
    EFI_PHYSICAL_ADDRESS Start;       // 起始物理位址
    EFI_PHYSICAL_ADDRESS End;         // 結束物理位址
    UINT64          Attribute;        // 記憶體屬性
} MEMORY_MAP_ENTRY;

MEMORY_MAP_ENTRY 結構與串連

MEMORY_MAP_ENTRY Signature: 'mmap' LIST_ENTRY Link Type: EfiConventionalMemory Start: 0x00100000 End: 0x00FFFFFF Attribute: WB, UC MEMORY_MAP_ENTRY Signature: 'mmap' LIST_ENTRY Link Type: EfiBootServicesData Start: 0x01000000 End: 0x01FFFFFF Attribute: WB MEMORY_MAP_ENTRY Signature: 'mmap' LIST_ENTRY Link Type: EfiRuntimeServicesData Start: 0x02000000 End: 0x020FFFFF Attribute: WB, RT gMemoryMap (List Head)
Conventional Memory Boot Services Runtime Services

1.3 EFI_MEMORY_TYPE 記憶體類型

EfiReservedMemoryType

保留記憶體,不可使用

EfiLoaderCode

OS Loader 程式碼

EfiLoaderData

OS Loader 資料

EfiBootServicesCode

Boot Services 程式碼

EfiBootServicesData

Boot Services 資料

EfiRuntimeServicesCode

Runtime Services 程式碼

EfiRuntimeServicesData

Runtime Services 資料

EfiConventionalMemory

可用的空閒記憶體

EfiACPIReclaimMemory

ACPI 可回收記憶體

EfiACPIMemoryNVS

ACPI NVS 記憶體

EfiMemoryMappedIO

記憶體映射 I/O

EfiPersistentMemory

持久性記憶體

2. 記憶體初始化配置圖

2.1 系統記憶體整體佈局

在 UEFI 系統啟動過程中,記憶體會被劃分為不同的區域。以下是一個典型的記憶體佈局範例:

0xFFFFFFFF 0xFED00000 0xA0000000 0x80000000 0x10000000 0x01000000 0x00100000 0x000A0000 0x00000000
MMIO Region PCIe, LAPIC, IO-APIC
SMRAM (SMM) System Management Mode ~8MB - 32MB
PCI Memory Hole PCI 設備記憶體映射
DXE Memory Pool DXE 驅動程式、Protocols 主要可用記憶體區域
Runtime Services Runtime Code & Data
Boot Services Boot Code & Data
Legacy Region VGA Buffer, Option ROMs
Low Memory IVT, BDA, EBDA
4GB
~4GB
TSEG
2GB-4GB
256MB-2GB
16MB
1MB-16MB
640KB
0KB

2.2 記憶體映射表初始狀態

系統啟動後,記憶體管理器會建立一個 Memory Map 鏈結串列來追蹤所有記憶體區域的狀態:

gMemoryMap (List Head) Reserved Start: 0x00000000 End: 0x0009FFFF Pages: 160 MMIO Start: 0x000A0000 End: 0x000FFFFF Pages: 96 Conventional Start: 0x00100000 End: 0x0FFFFFFF Pages: 65280 RuntimeServices Start: 0x10000000 End: 0x100FFFFF Pages: 256 Conventional Start: 0x10100000 End: 0x7FFFFFFF Pages: 458496 Reserved (SMRAM) Start: 0xA0000000 End: 0xA1FFFFFF Pages: 8192 MMIO Start: 0xFED00000 End: 0xFFFFFFFF Pages: 4864

3. 記憶體配置流程

3.1 AllocatePages 配置流程

當呼叫 gBS->AllocatePages() 時,記憶體管理器會執行以下步驟來找到適合的記憶體區塊:

步驟 1 / 7

步驟 1:接收配置請求

應用程式呼叫 AllocatePages(AllocateAnyPages, EfiBootServicesData, 4, &Address)

請求配置 4 頁 (16KB) 的 Boot Services Data 記憶體

// 配置請求
EFI_STATUS Status;
EFI_PHYSICAL_ADDRESS Address;

Status = gBS->AllocatePages (
    AllocateAnyPages,           // 配置類型:任意位址
    EfiBootServicesData,        // 記憶體類型
    4,                          // 頁數 (4 * 4KB = 16KB)
    &Address                    // 輸出:配置的位址
);

3.2 配置演算法詳解

First-Fit 演算法

  1. 從 gMemoryMap 鏈結串列的頭部開始遍歷
  2. 對每個 MEMORY_MAP_ENTRY 檢查:
    • 類型是否為 EfiConventionalMemory(可用記憶體)
    • 大小是否足夠容納請求的頁數
    • 位址是否符合對齊要求
  3. 找到第一個符合條件的區塊後停止搜尋
  4. 分割該區塊,更新記憶體映射表

4. 記憶體釋放流程

4.1 FreePages 釋放流程

當呼叫 gBS->FreePages() 時,記憶體管理器會將記憶體歸還並嘗試合併相鄰的空閒區塊:

步驟 1 / 6

步驟 1:接收釋放請求

應用程式呼叫 FreePages(0x00200000, 4)

請求釋放位址 0x002000004 頁 記憶體

// 釋放請求
EFI_STATUS Status;

Status = gBS->FreePages (
    0x00200000,    // 要釋放的記憶體位址
    4              // 頁數 (4 * 4KB = 16KB)
);

4.2 記憶體合併 (Coalescing)

釋放記憶體後,管理器會檢查相鄰區塊是否也是空閒的,如果是則合併它們以減少碎片化:

合併前

Free
8 頁
Releasing
4 頁
Free
12 頁

合併後

Free
24 頁

5. Pool 記憶體配置

5.1 AllocatePool 配置流程

AllocatePool 用於配置小型記憶體區塊。它在已配置的頁面中管理 Pool,使用 POOL_HEAD 結構追蹤每個配置。

步驟 1 / 5

步驟 1:接收 Pool 配置請求

應用程式呼叫 AllocatePool(EfiBootServicesData, 128, &Buffer)


                    

5.2 Pool 結構詳解

typedef struct {
    UINT32          Signature;    // 'phd0'
    UINT32          Reserved;     // Padding
    EFI_MEMORY_TYPE Type;         // Memory type
    UINTN           Size;         // Allocation size (including header)
    CHAR8           Data[1];      // Start of user data
} POOL_HEAD;

6. 啟動階段記憶體時間線

6.1 記憶體映射演進

隨著系統啟動,記憶體映射會經歷不同的階段變化。點擊各階段查看該階段的記憶體狀態:

記憶體映射快照

7. S4 復甦記憶體映射

7.1 S4 復甦階段記憶體映射

點擊各階段,查看系統從休眠喚醒到重新交還給 OS 的記憶體狀態與映射變化。

S4 復甦記憶體映射快照

8. 配置策略比較

8.1 配置演算法比較

對相同的記憶體佈局和配置請求,不同的演算法會選擇不同的區塊。選擇演算法查看差異:

配置請求: 4 頁

9. 記憶體碎片化視覺化

9.1 碎片化熱力圖

互動式模擬記憶體配置與釋放,觀察碎片化程度的變化:

可用區塊數 1
最大可用區塊 64
碎片化比率 0%
總可用空間 64
操作次數 0

10. ExitBootServices 記憶體交接

10.1 啟動服務到 Runtime 交接

當 OS Loader 呼叫 ExitBootServices() 後,Boot Services 記憶體被回收,只有 Runtime 和 ACPI 區域保留:

ExitBootServices 之前

ExitBootServices 之後