物理页分配机制与实现详解
源码路径:
rk3588/kernel-6.1/mm/page_alloc.c、mmzone.c、vmstat.c
内核版本:Linux 6.1(RK3588 / ARM64)
文档目录:linuxDoc/mm/
目录
一、原理
1.1 伙伴系统(Buddy Allocator)
物理内存按 2^order 个连续页 组成块管理:
- order 0:单页(4KB,取决于
PAGE_SIZE) - order n:2^n 页连续块
- 分配:在对应 order 空闲链取块;若无则向更高 order 分裂
- 释放:与 buddy 合并,若 buddy 也空闲则升阶
优点:O(log N) 分配/释放;缺点:外部碎片(有足够空闲页但无足够连续块)。
1.2 Zone 分层
每个 NUMA node(RK3588 UMA 通常仅 node 0)划分为多个 zone:
| Zone | ARM64 典型用途 |
|---|---|
| DMA32 | 低 4GB,32 位 DMA 设备 |
| NORMAL | 主内存 |
| (CMA 区域) | 页带 PageCMA,平时可迁移,CMA 分配时 pin |
分配请求通过 zonelist 按策略遍历 zone(本地 node 优先、fallback 等)。
1.3 水位线(Watermark)
每个 zone 维护 min / low / high 三档空闲页阈值:
| 水位 | 行为 |
|---|---|
| free > high | 正常分配 |
| free ≤ low | 唤醒 kswapd 异步回收 |
| free ≤ min | 分配器在慢路径 同步 direct reclaim |
| 仍不足 | compaction → 可能 OOM |
1.4 GFP 与迁移类型
gfp_t 指定分配上下文:是否可睡眠、是否 IO/FS、是否 DMA32、是否 可迁移(MIGRATE_MOVABLE) 等。
迁移类型(migratetype) 将空闲页分类(不可迁移、可迁移、可回收、CMA 等),避免不可移动内核数据阻塞 compaction。
1.5 Per-CPU 页缓存(PCP)
为降低 zone->lock 竞争,每个 CPU 有 per-cpu pageset:小 order 分配优先从本地列表取,批量 refill/drain。
二、实现方式
2.1 总入口:__alloc_pages()
1 | struct page *__alloc_pages(gfp_t gfp, unsigned int order, int preferred_nid, |
快路径:get_page_from_freelist() 在水位允许时从 zonelist 取页。
慢路径:__alloc_pages_slowpath() → direct reclaim、try_to_compact_pages、再次尝试 freelist,最后 __alloc_pages_may_oom()。
2.2 prepare_alloc_pages
构建 struct alloc_context:
- 解析 preferred nid、nodemask(NUMA/cpuset)
- 计算 alloc_flags(
ALLOC_WMARK_*、ALLOC_CPUSET等) - 选择起始 zoneref
2.3 get_page_from_freelist
对每个候选 zone:
- 检查 水位 + cpuset + spread 等
- 调用
rmqueue()从 buddy 或 PCP 取页 prep_new_page()初始化页状态
失败则尝试下一个 zone 或降低 watermark 等级(ALLOC_NO_WATERMARKS 仅特殊 GFP)。
2.4 rmqueue 与伙伴合并
__rmqueue_smallest:从最小满足 order 的 free_area 取expand():分裂大块__free_one_page()/free_pages():释放时buddy = pfn ^ (1 << order)尝试合并
2.5 慢路径概要
1 | __alloc_pages_slowpath |
2.6 初始化
| 阶段 | 函数 | 说明 |
|---|---|---|
| zone 建立 | free_area_init() |
各 zone 的 free_area、水位 |
| 页进入 buddy | memblock_free_all 间接 |
见 01 文档 |
| PCP | page_alloc_init() |
CPU hotplug 注册 page_alloc_cpu_online/dead |
2.7 vmstat.c
维护 NR_FREE_PAGES、NR_ALLOC_BATCH、per-zone LRU 统计等,供 /proc/vmstat、/proc/zoneinfo 与回收逻辑读取。
三、关键数据结构与接口
| 结构/接口 | 说明 |
|---|---|
struct zone |
free_area[]、watermark、percpu pageset |
struct free_area |
按 order 的空闲链表 |
__alloc_pages |
页分配核心 |
alloc_pages |
包装,返回 page |
__get_free_pages |
返回内核虚拟地址(非 highmem) |
free_pages / __free_pages |
释放 |
order 上限:MAX_ORDER(通常 11,即 4MB 块,与配置相关)。
四、RK3588 平台说明
- 编解码/显示驱动大块 DMA 常需 高 order 或 CMA;buddy 高 order 失败时依赖 compaction(见 07 文档)。
__GFP_DMA32:限制从 DMA32 zone 分配,避免 32 位设备无法访问高物理地址。- CMA 页:带
PageCMA,普通__alloc_pages在无 CMA 标志时不应长期占用 CMA 池(由 migratetype 与 gfp 约束)。
五、调试与观测
| 接口/参数 | 内容 |
|---|---|
/proc/buddyinfo |
各 zone 各 order 空闲块数 |
/proc/pagetypeinfo |
迁移类型分布 |
/proc/zoneinfo |
水位线与统计 |
min_free_kbytes |
sysctl,影响 min 水位 |
watermark_scale_factor |
影响 low/high 计算 |
tracepoint:trace_mm_page_alloc。
附录:源码索引
| 主题 | 文件 | 约略行号 |
|---|---|---|
| __alloc_pages | page_alloc.c | 5596 |
| page_alloc_init | page_alloc.c | 8733 |
| free_area_init | page_alloc.c | 8393 |
| zone 统计 | vmstat.c | 全文 |
正在加载留言…