你不是一个人
内核中的buddy system可谓是久仰大名,如雷贯耳。这年头找对象不容易,看看人内核里面是怎么找的,咱也好好学学,取取经~
敬个礼,握握手,你是我的好朋友
好了,说正经的,人是怎么找的呢?
__free_one_page()
...
while (order < max_order - 1) {
buddy_idx = __find_buddy_index(page_idx, order);
buddy = page + (buddy_idx - page_idx);
if (!page_is_buddy(page, buddy, order))
goto done_merging;
/*
* Our buddy is free or it is CONFIG_DEBUG_PAGEALLOC guard page,
* merge with it and move up one order.
*/
if (page_is_guard(buddy)) {
clear_page_guard(zone, buddy, order, migratetype);
} else {
list_del(&buddy->lru);
zone->free_area[order].nr_free--;
rmv_page_order(buddy);
}
combined_idx = buddy_idx & page_idx;
page = page + (combined_idx - page_idx);
page_idx = combined_idx;
order++;
}
...
其中重要的就是__find_buddy_index()。
/*
* 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);
}
人注释写得真好,还有个例子给你看。我这个人有个毛病,一旦看懂了代码就老兴奋了。忍不住多说两句,估计人真正的大牛对这种代码了如指掌,根本没兴趣了。
我个人看完这个代码有两点体会:
* 使用某个位置上的数做异或就像是一个开关,原来有的就清,没的就置位
* 两个小伙伴的上一级伙伴,是这个位为零的那个
举个栗子
+--------+--------+--------+--------+
|8 |9 |10 |11 |
+--------+--------+--------+--------+
[====== 2 =====] [===== 2 ======]
[############### 4 ###############]
举个栗子, 从上面这个图看出,O为1的时候,(8,9)为一组,(10,11)为一组。
8 ^ 2 = 0b 1000 ^ 0b 0010 = 0b 1010 = 10
10 ^ 2 = 0b 1010 ^ 0b 0010 = 0b 1000 = 8
可以看到,异或的结果就是order 2的那一位去反。很有意思。恩,是不是可以理解为男生找女生,女生找男生呢? (如果你硬要找一样的,我就没办法了)
那我们再来看如何找上一级的伙伴。从图上直观来看, (8,9) (9,10)的上一级就是数量为4的组。那他的首页面index就是8。
好了看看代码实现的效果
8 & ~2 = 0b 1000 & 0b 1101 = 0b 1000 = 8
10 & ~2 = 0b 1010 & 0b 1101 = 0b 1000 = 8
直观的效果就是大家都将order 2的那一位清除。哇哦,so easy.
大家注意一下实际代码中的一句
combined_idx = buddy_idx & page_idx;
page_idx = combined_idx;
在循环中,page_idx将作为下一次循环要去寻找伙伴的索引。那你想到了啥?
对了,正是因为自己的上一级伙伴是把order 2的那一位清空,所以自己和自己小伙伴做与运算就得到了上一级~
好了,我想你知道内核页分配中伙伴是怎么找的了~
到这里,就到这里,休息休息~