buddy找伙伴块算法(linux 3.10.102)
背景
本文描述内核找伙伴块的算法,根据给定order及page_idx(buddy块中第一个page的index),找到伙伴块的page_idx。如果需要查阅buddy的原理知识,请参考其他资料。
算法分析
内核找伙伴块的代码如下:
mm/page_alloc.c
/* * 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 */ static inline unsigned long __find_buddy_index(unsigned long page_idx, unsigned int order) { return page_idx ^ (1 << order); } |
参数 page_idex为需要释放的个pages中的第一个page在mem_map数组中的下标。
order为1时(下图中连续灰白颜色的个4个page,灰白部分互为buddy):
page [0] [1]与page [2] [3]互为伙伴块;page [4] [5]与page [6] [7]互为伙伴块;以此类推。第一个伙伴块中第一个页框index为2*的倍数。
buddy算法关注伙伴块中的第一个page,所以我们可以这样说,以0和2代表的block为伙伴块,以4和6为代表的block互为伙伴块。从规律上看,给定一个block中第一个page的index,它的伙伴块中的第一个page的索引buddy_index只有两种可能:
- buddy_index = page index + ,即向上加个page
- buddy_index = page index - ,即向下减个page
还可以推出另一个规律,见下图(同一个颜色表示伙伴块):
page index | 0 | 2 | 4 | 6 | 8 | 10 | 12 | 14 |
二进制 | 0000 | 0010 | 0100 | 0110 | 1000 | 1010 | 1100 | 1110 |
从二进制可以看出,互为伙伴块的各块中first page index区别在于第order位不同,该位为0表示伙伴块中的第一个块,为1表示伙伴块中的第二个块。根据上面的计算公式,该位为0的块中第一个page index加个page、该位为1的块中第一个page index减个page,就能找到对应的伙伴块。从数学上说加即是把第order位置1,减即是把order位置0,相当于:把块中的第一个page的index的第order位取反即得到对应伙伴块中第一个page的index(从上图中直接分析二进制也可以总结出这个规律)。
也就是说,为了求page_idx(伙伴块中第一个page的index)的伙伴块,需要先求出page_idx第order本来的值,然后取反就得到page_idx的伙伴块,但比较浪费时间。可以在此基础上推导出更快的方法,按照上面分析,
page_idx第order位 | 伙伴块page_idx第order位 |
0 | 1 |
1 | 0 |
从上表可以看出,第一列的bit值与1异或,就可以得到第二列的值。内核代码就是采用了异或的方法,即文章开头的函数:
static inline unsigned long __find_buddy_index(unsigned long page_idx, unsigned int order) { return page_idx ^ (1 << order); } |