__rmqueue_smallest()是伙伴系统分配路径的核心函数,负责从指定迁移类型的空闲链表中,找到最接近请求阶数的页块并分配出去。
核心作用
它实现了伙伴系统"最小可用块"的分配策略:从请求的order开始,逐级向上查找第一个非空的空闲链表,取出页块,若块大于所需则拆分。这种策略尽量减少对大块的浪费,有利于保持内存连续性。
参数解析
| 参数 | 含义 |
|---|---|
zone | 目标内存区域 |
order | 请求的分配阶数 |
migratetype | 迁移类型(决定从哪条链表分配) |
核心流程
典型实现如下:
static __always_inline struct page *__rmqueue_smallest(struct zone *zone, unsigned int order, int migratetype) { unsigned int current_order; struct free_area *area; struct page *page; /* 从请求阶数开始,逐级向上查找 */ for (current_order = order; current_order < MAX_ORDER; ++current_order) { area = &zone->free_area[current_order]; /* 查看该阶数、该迁移类型是否有空闲块 */ page = get_page_from_free_area(area, migratetype); if (!page) continue; /* 找到了,从链表移除并拆分 */ page_del_and_expand(zone, page, order, current_order, migratetype); trace_mm_page_alloc_zone_locked(page, order, migratetype); return page; } return NULL; }逐步说明
1. 从请求阶数开始向上查找
for (current_order = order; current_order < MAX_ORDER; ++current_order)
先看请求的
order是否有空闲块;没有就试
order + 1,直到MAX_ORDER - 1。这保证优先使用最小的可用块,减少对大块的浪费。
2. 查看目标迁移类型的链表
area = &zone->free_area[current_order]; page = get_page_from_free_area(area, migratetype); if (!page) continue;
get_page_from_free_area():前面讲的,查看该链表首个页块(不摘除)。链表为空则试下一阶。
3. 从链表删除并拆分
page_del_and_expand(zone, page, order, current_order, migratetype);
page_del_and_expand():__del_page_from_free_list()从链表摘除整个高阶块;expand()拆分,多余部分归还低阶链表;account_freepages()一次性更新统计。
最终
page指向恰好order阶的页块。
4. 返回页块
return page;
返回分配到的页块首页;
若遍历完所有阶都没找到,返回
NULL。
在分配路径中的位置
它是__rmqueue()的首选分配路径:
__rmqueue(zone, order, migratetype, alloc_flags) ├── __rmqueue_smallest(zone, order, migratetype) // ← 首先尝试 │ └── 从同迁移类型链表中找最小可用块 └── if (!page) __rmqueue_fallback(zone, order, migratetype, ...) // 降级:从其他类型偷
关键设计点
1. 只操作指定迁移类型
它只在
migratetype对应的链表中查找。如果该类型没有空闲块,返回
NULL,由__rmqueue_fallback()负责从其他类型"偷"页。
2. "Smallest" 的含义
优先用最小的可用块,避免过度拆分大块。
例如请求 order 3,如果有 order 3 空闲块就直接用;只有没有时才拆 order 4/5…。
3. 与__rmqueue_fallback()的分工
__rmqueue_smallest():同类型内查找;__rmqueue_fallback():跨类型偷取。
4. 迁移类型 vs pageblock 迁移类型
这里的
migratetype是请求的迁移类型;分配后可能通过
set_pageblock_migratetype()更新 pageblock 的实际类型。
总结
__rmqueue_smallest()是伙伴系统同类型分配的核心:它从请求阶数开始逐级向上扫描free_area,找到第一个非空的同迁移类型链表,用page_del_and_expand()取出并拆分出恰好所需的页块。它体现了"最小可用块"策略,尽量减少对大块的浪费;如果同类型无可用块,则交由__rmqueue_fallback()跨类型偷取。