伙伴系统中迁移类型降级(fallback)的优先级表。当某种迁移类型的空闲链表耗尽时,分配器按这个表尝试从其他类型"偷"页。
一、背景:为什么需要 fallback?
迁移类型与反碎片
空闲页按迁移类型分组(
MIGRATE_UNMOVABLE/MOVABLE/RECLAIMABLE等)。分配时优先从同类型链表取页,保持同类页聚集,减少碎片。
__rmqueue_smallest()负责同类型分配。
问题:同类型耗尽怎么办?
如果请求
MIGRATE_UNMOVABLE,但MIGRATE_UNMOVABLE链表空了,不能直接失败——那会导致分配失败、系统不可用。必须允许从其他迁移类型借用页块。
但借用会破坏反碎片:把可移动页块借给不可移动用途,混合后难以整块迁移/回收。
解决:有策略的 fallback
不随便借,而是按预定义的优先级尝试。
优先借"影响最小"的类型。
这就是
fallbacks[][]数组的作用。
二、数组定义解析
static int fallbacks[MIGRATE_PCPTYPES][MIGRATE_PCPTYPES - 1] = { [MIGRATE_UNMOVABLE] = { MIGRATE_RECLAIMABLE, MIGRATE_MOVABLE }, [MIGRATE_MOVABLE] = { MIGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE }, [MIGRATE_RECLAIMABLE] = { MIGRATE_UNMOVABLE, MIGRATE_MOVABLE }, };1. 维度
[MIGRATE_PCPTYPES][MIGRATE_PCPTYPES - 1]
第一维:请求的迁移类型(
MIGRATE_PCPTYPES种,即参与 PCP/分组的前几种)。第二维:候选的 fallback 类型列表,长度为
MIGRATE_PCPTYPES - 1(排除自己)。用指定初始化器
[MIGRATE_UNMOVABLE] = {...},清晰对应每种类型。
2. 注释
/* * This array describes the order lists are fallen back to when * the free lists for the desirable migrate type are depleted * * The other migratetypes do not have fallbacks. */
"the order lists are fallen back to":当目标类型链表耗尽时,按此顺序尝试其他类型。
"The other migratetypes do not have fallbacks":
CMA、ISOLATE等不参与 fallback:MIGRATE_CMA:CMA 区域有特殊用途,不能被普通分配随意借用(但可反向:普通类型可借 CMA?实际有特殊处理);MIGRATE_ISOLATE:隔离区,绝对不能借用。
所以数组第一维只覆盖
MIGRATE_PCPTYPES。
3. 三行内容
| 请求类型 | 第一优先 fallback | 第二优先 fallback |
|---|---|---|
MIGRATE_UNMOVABLE | MIGRATE_RECLAIMABLE | MIGRATE_MOVABLE |
MIGRATE_MOVABLE | MIGRATE_RECLAIMABLE | MIGRATE_UNMOVABLE |
MIGRATE_RECLAIMABLE | MIGRATE_UNMOVABLE | MIGRATE_MOVABLE |
三、优先级的设计逻辑
核心原则:借"影响最小"的类型
不同迁移类型的"可移动性"不同:
MIGRATE_MOVABLE:可移动(用户态页),最灵活,可迁移/回收。MIGRATE_RECLAIMABLE:可回收(slab 缓存),可回收但不可迁移。MIGRATE_UNMOVABLE:不可移动(内核结构),既不可迁移也不可回收,最"死"。
破坏顺序(从轻到重):
借
RECLAIMABLE:最坏情况是回收,页块仍可能恢复;借
MOVABLE:可迁移,但可能造成碎片;借
UNMOVABLE:最糟糕,永久占据,破坏连续性。
逐行分析
MIGRATE_UNMOVABLE的 fallback
[MIGRATE_UNMOVABLE] = { MIGRATE_RECLAIMABLE, MIGRATE_MOVABLE },请求不可移动页,但不可移动链表空了。
优先借
RECLAIMABLE:可回收,未来能还回来;再借
MOVABLE:可迁移,影响次之;最后才考虑(虽然表里只列了两个,实际逻辑可能还有更多处理)。
不会借
UNMOVABLE(自己),因为已经空了。
MIGRATE_MOVABLE的 fallback
[MIGRATE_MOVABLE] = { MIGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE },请求可移动页,但可移动链表空了。
优先借
RECLAIMABLE:可回收;再借
UNMOVABLE:不可移动,但至少能用;顺序反映:宁可借不可移动,也不……(这里没有更多选项)。
MIGRATE_RECLAIMABLE的 fallback
[MIGRATE_RECLAIMABLE] = { MIGRATE_UNMOVABLE, MIGRATE_MOVABLE },请求可回收页,但可回收链表空了。
优先借
UNMOVABLE:不可移动但稳定;再借
MOVABLE:可移动;为什么优先
UNMOVABLE?因为 RECLAIMABLE 本身不可迁移,借UNMOVABLE语义更接近(都不可迁移),借MOVABLE反而可能把可移动页块"污染"成不可回收用途。
设计哲学总结
尽量借"能还回来"的类型:
RECLAIMABLE优先(可回收);其次借"影响小"的类型;
避免借"不可逆"的类型,除非别无选择。
四、在 fallback 路径中的使用
__rmqueue_fallback()
static inline struct page *__rmqueue_fallback(struct zone *zone, int order, int start_migratetype, unsigned int alloc_flags) { int current_order, i; struct page *page; struct free_area *area; int migratetype; int fallback_mt; /* * 从最高阶往下找,找能拆出 order 的块 */ for (current_order = MAX_ORDER - 1; current_order >= order; --current_order) { area = &zone->free_area[current_order]; /* * 按 fallbacks 数组顺序尝试其他迁移类型 */ for (i = 0; i < MIGRATE_PCPTYPES - 1; i++) { fallback_mt = fallbacks[start_migratetype][i]; /* 跳过被隔离的类型 */ if (is_migrate_isolate(fallback_mt)) continue; /* 尝试从该类型取页 */ page = get_page_from_free_area(area, fallback_mt); if (!page) continue; /* 找到,取走并拆分 */ del_page_from_free_list(page, zone, current_order, fallback_mt); ... /* * 把该 pageblock 的迁移类型改为请求类型, * 或做其他反碎片处理 */ ... return page; } } return NULL; }关键点
按
fallbacks[start_migratetype]顺序尝试:i = 0:第一优先 fallback;i = 1:第二优先;依次尝试。
跳过隔离类型:
is_migrate_isolate(fallback_mt)检查,隔离区不能借。
借用后可能改变迁移类型:
把借来的 pageblock 标记为请求类型,反映实际用途;
或做其他"反碎片"处理(如判断是否值得偷整个 pageblock)。
从高阶往下找:
优先用高阶块(可以拆分成多个小块,更灵活);
避免把小块的连续性破坏。
五、与前面内容的呼应
| 前面出现过的 | 这里的关联 |
|---|---|
migratetype_names[] | MIGRATE_*的名字表 |
__rmqueue_smallest() | 同类型分配;失败后走 fallback |
__rmqueue_fallback() | 使用fallbacks[][]表 |
MIGRATE_PCPTYPES | 数组第一维大小 |
is_migrate_isolate() | 跳过隔离类型 |
MIGRATE_CMA/MIGRATE_ISOLATE | 不参与 fallback |
set_pageblock_migratetype() | 借用后可能更新类型 |
| 反碎片 | fallback 策略的核心目标 |
调用链:
__rmqueue() ├── __rmqueue_smallest() // 同类型 └── if (!page) __rmqueue_fallback() // 跨类型 └── for (i = 0; i < MIGRATE_PCPTYPES - 1; i++) fallback_mt = fallbacks[start_migratetype][i] ...
六、小结表
| 项 | 说明 |
|---|---|
| 数组名 | fallbacks |
| 维度 | [MIGRATE_PCPTYPES][MIGRATE_PCPTYPES - 1] |
| 作用 | 定义各迁移类型的降级借用优先级 |
| 第一维 | 请求的迁移类型 |
| 第二维 | 候选 fallback 类型,按优先级排列 |
| 不参与的类型 | CMA、ISOLATE("The other migratetypes do not have fallbacks") |
| 设计原则 | 优先借"可回收/影响小"的类型 |
| 使用点 | __rmqueue_fallback() |
| 可见性 | static,文件内私有 |
三行速查
| 请求类型 | 优先借 | 其次借 |
|---|---|---|
| UNMOVABLE | RECLAIMABLE | MOVABLE |
| MOVABLE | RECLAIMABLE | UNMOVABLE |
| RECLAIMABLE | UNMOVABLE | MOVABLE |
一句话总结
fallbacks[][]定义了伙伴系统中迁移类型的降级借用优先级:当请求类型的空闲链表耗尽时,按此表依次尝试从其他类型"偷"页;它只覆盖MIGRATE_PCPTYPES三种类型(CMA/ISOLATE不参与),设计原则是优先借"可回收、影响小"的类型(如RECLAIMABLE),以尽量减少对反碎片的破坏。