📅  最后修改于: 2023-12-03 14:49:32.987000             🧑  作者: Mango
伙伴系统(Buddy System)是一种用于管理内存分配和回收的技术,它通过将内存划分为不同的大小类别,并以2的幂次方为单位来分配和回收内存。伙伴系统的原理是将内存按照大小分成不同的块,将大小相同、相邻的块放在一起,形成一棵完美的二叉树,树的根节点是整个可用内存的大小。伙伴系统支持快速分配和回收内存,在内存管理中应用广泛。
快速分配和回收内存:伙伴系统可以快速地分配和回收内存,因为它只需要在二叉树中搜索相应的内存块,而不需要扫描整个内存空间。这样可以大大减少分配和回收内存的时间。
内存碎片化低:伙伴系统可以将内存分配成大小相等的块,这样可以大大减少内存碎片化的问题。
可以提高内存使用效率:伙伴系统可以更好地利用可用内存,因为它可以按需分配和回收内存块,而不是一次性分配一大块内存,这样可以提高内存使用效率。
伙伴系统的原理是将可用的内存按照大小划分成不同的块,然后将相邻、大小相同的块放在一起,形成一棵完美的二叉树。每个节点表示一个内存块,节点的左儿子表示该节点的一半大小的内存块,右儿子表示同样大小的伙伴内存块。当一个内存块被分配出去时,它被标记为已使用,并将其伙伴块标记为已分配。当一个内存块被释放时,其伙伴块被检查,如果未被使用,则将两个伙伴块合并成一个更大的内存块,并递归进行合并,直到找到一个已分配的伙伴块或者到达根节点。
伙伴系统的实现需要考虑以下几个方面:
为了保证内存块大小能够满足2的幂次方的要求,实现中通常会对内存块的大小进行对齐。例如,如果要分配一个大小为64字节的内存块,那么实际分配的内存大小可能会是128字节或256字节。
伙伴系统通常会在内存池上实现。内存池的大小是有限的,因此需要在内存分配和回收时进行管理和维护。例如,内存块的大小不能超过内存池的大小,并且需要避免内存池溢出。
伙伴系统的效率和复杂度都比较高,但也存在一定的问题。例如,当内存块的大小过小时,可能会导致内存浪费和效率低下。此外,伙伴系统的实现也需要考虑空间的效率和可扩展性问题。
#define PAGE_SHIFT 12
#define PAGE_SIZE (1UL << PAGE_SHIFT)
#define PAGE_MASK (~(PAGE_SIZE-1))
#define BUDDY_MAX_ORDER 11 /* 最大的块的大小 */
#define BUDDY_MIN_ORDER 4 /* 最小的块大小*/
#define BUDDY_START_ADDR 0x400000 /* 内存池的起始地址 */
/**
* 定义伙伴结构
*/
struct Buddy {
int next; /* 指向下一个块 */
int prev; /* 指向上一个块 */
} buddy[1 << (BUDDY_MAX_ORDER)];
/**
* 在二叉树中查找大小为 order 的伙伴块
*/
int buddy_find_order(int size)
{
int order = BUDDY_MAX_ORDER - 1;
if (size > PAGE_SIZE)
return -1;
while (order) {
if (size & (1UL << order))
break;
order--;
}
return order;
}
/**
* 申请一个大小为 size 的伙伴块
*/
void *buddy_alloc(int size)
{
int order;
int curr;
int buddy_idx;
int i;
void *ret;
if (!size)
return NULL;
order = buddy_find_order(size);
for (i = order; i < BUDDY_MAX_ORDER; i++) {
if (!buddy[i].next)
continue;
curr = buddy[i].next;
buddy_del(curr, i);
buddy_idx = buddy_newindex(curr, i - 1);
break;
}
if (i == BUDDY_MAX_ORDER) {
return NULL;
}
ret = (void *)(((curr - BUDDY_START_ADDR) << PAGE_SHIFT));
return ret;
}
/**
* 释放一个伙伴块
*/
void buddy_free(void *ptr)
{
int block;
int order;
int buddy_idx;
block = ((unsigned long)ptr >> PAGE_SHIFT) + BUDDY_START_ADDR;
order = BUDDY_MIN_ORDER;
while (1) {
buddy_idx = buddy_newindex(block, order);
if (buddy[buddy_idx].next || block == 0)
break;
buddy_add(buddy_idx, order);
block = block & ((1UL << order) - 1);
order++;
}
buddy_add(buddy_idx, order);
}