设置空闲伙伴页块的阶数。它是在页块被释放进伙伴系统(或从伙伴系统取出)时,用来在struct page的private字段里记录"这个空闲块有多大"的辅助函数。
一、函数签名
static inline void set_buddy_order(struct page *page, unsigned int order)
参数
| 参数 | 类型 | 含义 |
|---|---|---|
page | struct page * | 页块的首页(head page) |
order | unsigned int | 该空闲页块的阶数 |
返回值
void。
static inline
文件内私有 + 强制/建议内联。
在伙伴系统的分配/释放/合并热路径上频繁调用,必须零开销。
二、背景:空闲页块的阶数存在哪?
struct page的private字段
struct page有一个联合体字段,其中一个成员是:
struct { ... unsigned long private; ... };private是一个unsigned long,不同场景下复用:在buffer_head场景,指向 buffer_head;
在slab场景,指向 slab 相关结构;
在伙伴系统空闲页场景,存 order。
关键约定
当一个页块空闲、挂在伙伴系统
free_area[order]链表上时:它的首页(head page)的
private字段存order;这样从链表取出页块时,能立刻知道它有多大。
当页块被分配出去后,
private可能被其他用途复用(如 buffer_head),所以 order 信息不再有效。
set_buddy_order()就是写这个字段
static inline void set_buddy_order(struct page *page, unsigned int order) { set_page_private(page, order); __SetPageBuddy(page); }逐步说明
1. 记录 order
set_page_private(page, order);
set_page_private(page, value)是内核提供的封装:把
page->private设为value;通常还带一些调试/类型检查(如
VM_BUG_ON_PAGE)。
这里把
order写进private。
2. 标记为 Buddy 页
__SetPageBuddy(page);
设置
PG_buddy标志位(__SetPageBuddy是底层、不加额外检查的版本)。表示"这个页当前是伙伴系统里的空闲页"。
这是状态标记,用于:
free_pages_prepare()时检测双重释放(已 Buddy 又被释放);bad_page()检测非法状态;区分"空闲伙伴页"与"已分配页"。
对应的读取/清除
读 order:
static inline unsigned int buddy_order(struct page *page) { VM_BUG_ON_PAGE(!PageBuddy(page), page); return page_private(page); }清除 Buddy 状态(页被分配出去时):
__ClearPageBuddy(page); set_page_private(page, 0);
或封装成
rmv_page_order()之类。
三、在伙伴系统中的位置
释放页块时(__free_one_page())
static inline void __free_one_page(struct page *page, unsigned long pfn, struct zone *zone, unsigned int order, int migratetype, fpi_t fpi_flags) { ... /* 合并伙伴,最终确定 order */ while (order < max_order - 1) { buddy_pfn = __find_buddy_pfn(pfn, order); buddy = page + (buddy_pfn - pfn); if (!page_is_buddy(page, buddy, order)) // 检查 buddy 是否空闲且同阶 break; /* 合并 */ list_del(&buddy->lru); ... order++; } ... /* 把合并后的页块加入 free_area[order] */ set_buddy_order(page, order); list_add(&page->lru, &zone->free_area[order].free_list[migratetype]); ... }合并完成后,用
set_buddy_order(page, order)记录最终阶数并标记 Buddy。然后挂到对应
free_area[order]链表。
分配页块时(__rmqueue_smallest()/expand())
从
free_area[order]取出页块后:rmv_page_order(page):清除 Buddy 状态和 private;若需要分裂(
expand()),把大块拆成小块,每块重新set_buddy_order()后放回对应链表。
page_is_buddy()检查
static inline int page_is_buddy(struct page *page, struct page *buddy, unsigned int order) { if (!PageBuddy(buddy)) return 0; if (buddy_order(buddy) != order) return 0; ... return 1; }判断"伙伴页是否空闲且同阶",才能合并。