内存管理-伙伴系统:__find_buddy_pfn 函数解析与伙伴查找机制
内存管理-伙伴系统:__find_buddy_pfn 函数解析与伙伴查找机制
文章目录
- 内存管理-伙伴系统:__find_buddy_pfn 函数解析与伙伴查找机制
- 伙伴系统合并机制概述
- __find_buddy_pfn 函数解析
- 伙伴系统初始化时的块分配规则
- 实例分析
- 合并场景补充
- 总结
伙伴系统合并机制概述
伙伴系统的核心价值在于通过空闲块的合并减少内存碎片,而合并的前提是精准定位当前块的“伙伴”。__find_buddy_pfn函数的作用正是计算给定页帧号(Page Frame Number, pfn)在指定阶(order)下的伙伴页帧号。仅当伙伴块与当前块阶数相同且均为空闲状态时,才会触发后续的合并逻辑。
linux-5.10.202/mm/page_alloc.c:1018buddy_pfn=__find_buddy_pfn(pfn,order);// 相邻的伙伴块,按照 ^ (1 << order) 计算__find_buddy_pfn 函数解析
__find_buddy_pfn是一个静态内联函数,通过位异或运算实现伙伴页帧号的快速计算,其源码与注释如下:
linux-5.10.202/mm/internal.h:156/* * Locate the struct page for both the matching buddy in our * pair (buddy1) and the combined O(n+1) page they form (page). * * 1) Any buddy B1 will have an order O twin B2 which satisfies * the following equation: * B2 = B1 ^ (1 << O) * For example, if the starting buddy (buddy2) is #8 its order * 1 buddy is #10: * B2 = 8 ^ (1 << 1) = 8 ^ 2 = 10 * * 2) Any buddy B will have an order O+1 parent P which * satisfies the following equation: * P = B & ~(1 << O) * * Assumption: *_mem_map is contiguous at least up to MAX_ORDER */staticinlineunsignedlong__find_buddy_pfn(unsignedlongpage_pfn,unsignedintorder){returnpage_pfn^(1<<order);}以 pfn 8(二进制1000)、order 1 为例:该块包含连续 2 个页(pfn 8 和 9)。通过位异或运算8 ^ (1 << 1),得到伙伴页帧号为 10,对应块包含 pfn 10 和 11。后续逻辑会验证伙伴块是否处于相同阶且空闲,若满足则合并为 order 2 的块。
伙伴系统初始化时的块分配规则
伙伴系统初始化通过__free_pages_memory函数将内存块按阶对齐规则加入空闲链表,确保每个块的起始 pfn 是2^order的整数倍。源码如下:
linux-5.10.202/mm/memblock.c:1900staticvoid__init__free_pages_memory(unsignedlongstart,unsignedlongend)// 伙伴系统初始化,会按照 page 的 pfn 按阶存放 每个阶存档的page地址一定是 2**n 对齐{intorder;while(start<end){order=min(MAX_ORDER-1UL,__ffs(start));// ffs 第一个有 1 的 bit 位置 - 1,相当于根据 pfn 地址确定 orderwhile(start+(1UL<<order)>end)// 和 1912 行的 start += (1UL << order); 有联系,如果有连续大块,放到对应 orderorder--;memblock_free_pages(pfn_to_page(start),start,order);// 加入到伙伴系统start+=(1UL<<order);}}函数逻辑拆解:
__ffs(start)返回start二进制表示中第一个置 1 位的位置(从 0 计数),用于确定初始阶数;- 通过内层循环动态降低阶数,确保块大小
(1 << order)不超过剩余内存范围(start到end); - 调用
memblock_free_pages将对齐后的块加入伙伴系统,并更新start指针处理下一段内存。
实例分析
以 pfn 1 到 13(共 12 个连续页)为例,展示初始化时的块分配逻辑:
| pfn | hex | __ffs | 加入到伙伴的阶 | 解释 |
|---|---|---|---|---|
| 1 | 1 | 0 | 0 | 1 + (1 << 0) <= 13 |
| 2 | 10 | 1 | 1 | 2 + (1 << 1) <= 13 |
| 3 | 11 | 1 | 跳过 | 2 += (1 << 1) |
| 4 | 100 | 2 | 2 | 4 + (1 << 2) <= 13 |
| 5 | 101 | 2 | 跳过 | 4 += (1 << 2) |
| 6 | 110 | 2 | 跳过 | 4 += (1 << 2) |
| 7 | 111 | 2 | 跳过 | 4 += (1 << 2) |
合并场景补充
若 pfn 3 变为 0 阶并释放,其伙伴为 pfn 2(计算:3 ^ (1 << 0) = 2),二者可合并为 1 阶块;同理,pfn 4 和 5 在 0 阶互为伙伴,合并为 1 阶后,可与 pfn 6-7 组成的 1 阶块进一步合并为 2 阶块(计算:4 ^ (1 << 1) = 6)。
总结
- 伙伴查找核心机制:
__find_buddy_pfn通过page_pfn ^ (1 << order)位异或运算,快速定位物理地址连续且对齐的伙伴页帧号,是伙伴系统合并逻辑的基础。 - 初始化对齐规则:
__free_pages_memory利用__ffs确定初始阶数,动态调整以适配内存范围,确保每个块按2^order对齐加入伙伴系统,为后续合并创造条件。 - 合并逻辑约束:仅当伙伴块阶数相同且均为空闲时,才会触发合并,通过逐步提升阶数有效减少内存碎片。
