graph TB
A["内存管理"] --> B["分配\nmalloc / kmalloc"]
A --> C["回收\nfree / kfree"]
A --> D["碎片\n内部 + 外部"]
B -.->|API| B1["进程 / 内核"]
C -.->|回收| C1["延迟释放 / 立即释放"]
D -.->|解决| D1["伙伴系统 + slab"]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#E8D5F5,stroke:#CE93D8,color:#333
style C fill:#FFDAB9,stroke:#FFAB76,color:#333
style D fill:#FFB3C6,stroke:#F48FB1,color:#333
3 个问题:
如何高效分配内存?
如何及时回收内存?
如何减少碎片?
一、页框(Page Frame)
1.1 什么是页框?
graph LR
A["虚拟地址"] -->|"MMU 翻译"| B["物理页帧"]
B --> C["4 KB / 2 MB / 1 GB"]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#E8D5F5,stroke:#CE93D8,color:#333
style C fill:#FFDAB9,stroke:#FFAB76,color:#333
graph TB
A["空闲页"] --> B["> HIGH\n空闲"]
A --> C["LOW ~ HIGH\n轻压"]
A --> D["MIN ~ LOW\n中压\nkswapd 唤醒"]
A --> E["< MIN\n紧急\n直接回收"]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#B5EAD7,stroke:#80CBC4,color:#333
style C fill:#FFDAB9,stroke:#FFAB76,color:#333
style D fill:#FFB3C6,stroke:#F48FB1,color:#333
style E fill:#F48FB1,stroke:#FFB3C6,color:#333
2.4 关键启示
NUMA = 多节点——每个 CPU 有”本地”内存
区域 = 不同性质的内存(DMA、普通、高端)
水位——控制内存压力响应
分配策略:本地节点 → 远端节点
三、伙伴系统(Buddy System)
3.1 为什么需要伙伴系统?
graph TB
A["外部碎片"] --> B["分配 4 块 64 KB"]
A --> C["中间用 32 KB 隔开"]
A --> D["释放中间\n但 64 KB 块仍不连续"]
B --> B1["❌ 无法分配 128 KB 大块"]
style A fill:#FFB3C6,stroke:#F48FB1,color:#333
style B fill:#FFDAB9,stroke:#FFAB76,color:#333
style C fill:#FFF9C4,stroke:#F9A825,color:#333
style D fill:#FFF9C4,stroke:#F9A825,color:#333
style B1 fill:#F48FB1,stroke:#FFB3C6,color:#333
伙伴系统的目标:把物理页按 2 的幂次组织,避免外部碎片。
3.2 伙伴系统原理
graph TB
A["分配阶 order 0\n1 页 (4KB)"] --> B["order 1\n2 页 (8KB)"]
B --> C["order 2\n4 页 (16KB)"]
C --> D["order 3\n8 页 (32KB)"]
D --> E["order 4\n16 页 (64KB)"]
E --> F["order 5\n32 页 (128KB)"]
F --> G["order 6\n64 页 (256KB)"]
G --> H["order 7\n128 页 (512KB)"]
H --> I["order 8\n256 页 (1MB)"]
I --> J["order 9\n512 页 (2MB)"]
J --> K["order 10\n1024 页 (4MB)"]
K --> L["MAX_ORDER - 1\n最大"]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#E8D5F5,stroke:#CE93D8,color:#333
style C fill:#FFDAB9,stroke:#FFAB76,color:#333
style D fill:#FFB3C6,stroke:#F48FB1,color:#333
style E fill:#B5EAD7,stroke:#80CBC4,color:#333
style F fill:#C7CEEA,stroke:#9FA8DA,color:#333
style G fill:#E8D5F5,stroke:#CE93D8,color:#333
style H fill:#FFDAB9,stroke:#FFAB76,color:#333
style I fill:#FFB3C6,stroke:#F48FB1,color:#333
style J fill:#B5EAD7,stroke:#80CBC4,color:#333
style K fill:#C7CEEA,stroke:#9FA8DA,color:#333
style L fill:#F48FB1,stroke:#FFB3C6,color:#333
graph TB
A["alloc_pages(order=2)"] --> B{"order 2 空闲?"}
B -->|"是"| C["分配 4 页"]
B -->|"否"| D{"order 3 空闲?"}
D -->|"是"| E["分配 8 页"]
E --> F["切分为 4+4"]
F --> G["返回前 4 页"]
F --> H["后 4 页挂回 order 2"]
D -->|"否"| I["向上检查 order 4..."]
I --> J["OOM / 失败"]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#FFF9C4,stroke:#F9A825,color:#333
style C fill:#B5EAD7,stroke:#80CBC4,color:#333
style D fill:#FFF9C4,stroke:#F9A825,color:#333
style E fill:#B5EAD7,stroke:#80CBC4,color:#333
style F fill:#FFDAB9,stroke:#FFAB76,color:#333
style G fill:#B5EAD7,stroke:#80CBC4,color:#333
style H fill:#B5EAD7,stroke:#80CBC4,color:#333
style I fill:#FFF9C4,stroke:#F9A825,color:#333
style J fill:#FFB3C6,stroke:#F48FB1,color:#333
3.4 释放算法(合并伙伴)
graph TB
A["释放 4 页"] --> B{"伙伴(后 4 页)\n也在 order 2?"}
B -->|"是"| C["合并为 8 页"]
C --> D{"伙伴(后 8 页)\n也在 order 3?"}
D -->|"是"| E["合并为 16 页"]
E --> F["递归向上"]
D -->|"否"| G["挂到 order 3 链表"]
B -->|"否"| H["挂到 order 2 链表"]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#FFF9C4,stroke:#F9A825,color:#333
style C fill:#B5EAD7,stroke:#80CBC4,color:#333
style D fill:#FFF9C4,stroke:#F9A825,color:#333
style E fill:#B5EAD7,stroke:#80CBC4,color:#333
style F fill:#FFF9C4,stroke:#F9A825,color:#333
style G fill:#B5EAD7,stroke:#80CBC4,color:#333
style H fill:#B5EAD7,stroke:#80CBC4,color:#333
graph TB
A["kmalloc(128)"] --> B{"size 适合 slab?"}
B -->|"是"| C["从 slab 缓存分配"]
B -->|"否"| D["伙伴系统分配\n按 2 的幂"]
C --> E["返回虚拟地址\n(连续物理)"]
D --> E
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#FFF9C4,stroke:#F9A825,color:#333
style C fill:#B5EAD7,stroke:#80CBC4,color:#333
style D fill:#FFDAB9,stroke:#FFAB76,color:#333
style E fill:#B5EAD7,stroke:#80CBC4,color:#333
graph TB
A["vmalloc(8MB)"] --> B["分配多个 order-3 页\n(共 1024 页,物理可不连续)"]
B --> C["建立页表\n虚拟地址连续"]
C --> D["返回虚拟地址"]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#E8D5F5,stroke:#CE93D8,color:#333
style C fill:#FFDAB9,stroke:#FFAB76,color:#333
style D fill:#B5EAD7,stroke:#80CBC4,color:#333
4.4 何时用哪个?
graph TB
A["需要分配内存"] --> B{"DMA 需要?"}
B -->|"是"| C["alloc_pages + GFP_DMA"]
B -->|"否"| D{"物理连续?"}
D -->|"是"| E["kmalloc"]
D -->|"否"| F{"大小 < 128 KB?"}
F -->|"是"| G["kmalloc (slab)"]
F -->|"否"| H["vmalloc"]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#FFF9C4,stroke:#F9A825,color:#333
style C fill:#FFDAB9,stroke:#FFAB76,color:#333
style D fill:#FFF9C4,stroke:#F9A825,color:#333
style E fill:#B5EAD7,stroke:#80CBC4,color:#333
style F fill:#FFF9C4,stroke:#F9A825,color:#333
style G fill:#B5EAD7,stroke:#80CBC4,color:#333
style H fill:#FFDAB9,stroke:#FFAB76,color:#333
4.5 关键启示
kmalloc:物理连续,限制 ~4 MB
vmalloc:虚拟连续,物理不连续,大块
alloc_pages:最底层,返回 struct page
选 API:DMA、连续性、大小
五、Slab 分配器(解决内部碎片)
5.1 什么是内部碎片?
graph TB
A["分配 1 字节\n实际给 8 字节"] --> B["浪费 7 字节\n内部碎片"]
style A fill:#FFB3C6,stroke:#F48FB1,color:#333
style B fill:#FFB3C6,stroke:#F48FB1,color:#333
伙伴系统问题:按 2 的幂次分配,小对象浪费大。
5.2 Slab 原理
graph TB
A["Slab 缓存\n(struct task_struct)"] --> B["Slab 1\n(多个对象)"]
A --> C["Slab 2\n(多个对象)"]
A --> D["Slab 3\n(多个对象)"]
B --> B1["对象 1"]
B --> B2["对象 2"]
B --> B3["..."]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#E8D5F5,stroke:#CE93D8,color:#333
style C fill:#E8D5F5,stroke:#CE93D8,color:#333
style D fill:#E8D5F5,stroke:#CE93D8,color:#333
style B1 fill:#FFF9C4,stroke:#F9A825,color:#333
style B2 fill:#FFF9C4,stroke:#F9A825,color:#333
style B3 fill:#FFF9C4,stroke:#F9A825,color:#333
graph LR
A["SLAB\nSolaris 风格"] --> B["SLUB\nLinux 默认"]
B --> C["SLOB\n嵌入式"]
style A fill:#FFB3C6,stroke:#F48FB1,color:#333
style B fill:#B5EAD7,stroke:#80CBC4,color:#333
style C fill:#FFDAB9,stroke:#FFAB76,color:#333
5.6 关键启示
Slab = 对象缓存——预分配 + 复用
3 种状态:FULL / PARTIAL / FREE
解决内部碎片——伙伴系统的补充
Linux 5.x+ 默认 SLUB
六、内存管理的”5 层模型”
graph TB
A["用户态\nmalloc / free"] --> B["glibc ptmalloc2"]
B --> C["内核 brk / sbrk / mmap"]
C --> D["内核\nkmalloc / vmalloc"]
D --> E["Slab\n(SLUB)"]
E --> F["Buddy\n(伙伴系统)"]
F --> G["物理页框\n(struct page)"]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#E8D5F5,stroke:#CE93D8,color:#333
style C fill:#FFDAB9,stroke:#FFAB76,color:#333
style D fill:#FFDAB9,stroke:#FFAB76,color:#333
style E fill:#FFB3C6,stroke:#F48FB1,color:#333
style F fill:#B5EAD7,stroke:#80CBC4,color:#333
style G fill:#FFF9C4,stroke:#F9A825,color:#333
graph TB
A["分配内存"] --> B{"上下文?"}
B -->|"进程上下文\n可睡眠"| C["GFP_KERNEL"]
B -->|"中断/软中断\n不可睡眠"| D["GFP_ATOMIC"]
B -->|"NUMA 亲和"| E["__GFP_THISNODE"]
C --> C1["可能触发回收"]
D --> D1["不能触发回收"]
E --> E1["只在当前节点"]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#FFF9C4,stroke:#F9A825,color:#333
style C fill:#B5EAD7,stroke:#80CBC4,color:#333
style D fill:#FFB3C6,stroke:#F48FB1,color:#333
style E fill:#FFDAB9,stroke:#FFAB76,color:#333
style C1 fill:#FFF9C4,stroke:#F9A825,color:#333
style D1 fill:#FFF9C4,stroke:#F9A825,color:#333
style E1 fill:#FFF9C4,stroke:#F9A825,color:#333
graph TB
A["内存管理基础核心"] --> B["页框\nstruct page"]
A --> C["节点/区域\nNUMA"]
A --> D["伙伴系统\nBuddy"]
A --> E["Slab\n对象缓存"]
A --> F["API 选型\nkmalloc/vmalloc"]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#E8D5F5,stroke:#CE93D8,color:#333
style C fill:#FFDAB9,stroke:#FFAB76,color:#333
style D fill:#B5EAD7,stroke:#80CBC4,color:#333
style E fill:#FFB3C6,stroke:#F48FB1,color:#333
style F fill:#FFF9C4,stroke:#F9A825,color:#333