自顶向下设计-伙伴系统
伙伴系统(Buddy system)
-
伙伴系统是内核中用来管理物理内存的一种算法,我们知道内存中有一些是被内核代码占用,还有一些是被特殊用途所保留,那么剩余的空闲内存都会交给内核内存管理系统来进行统一管理和分配,内核中会把内存按照页来组织分配,随着进程的对内存的申请和释放,系统的内存会不断的区域碎片化,到最后会发现,明明系统还有很多空闲内存,却无法分配出一块连续的内存,这对于系统来说并不是好事。而伙伴系统算法就是为了缓解这种碎片化。
-
伙伴系统(buddy system)把系统中要管理的物理内存按照页面个数分为不同的组,确切来说是分成了 11 个组,分别对应 11 种大小不同的连续内存块,每组中的内存块大小都相等,为 2 的幂次个物理页。那么系统中就存在 2 ^ 0~2 ^ 10 这么 11 种大小不同的内存块,对应内存块大小为 4KB ~ 4KB * 2^10。也就是 4KB ~ 4M。内核用 11 个链表来管理 11 种大小不同的内存块。
实验要求
1. 实验描述:
假设内存总大小为 2^10=1024,开始地址为 0,结束地址为 1023,在系统初始时,整个内存是 1 块空闲内存分区,大小为 2^10=1024。有 n 个进程需要分配内存空间。使用伙伴系统(Buddy System)算法来依次为这 n 个进程分配内存。当内存分配完毕之后,再将分配的这 n 个空闲分区块进行合并回收,最后得到 1 整块大的空闲分区,其大小为 2^10=1024。
2. 实验输出:
(1)在内存分配过程中,输出每一次内存分配结果,即空闲/占用分区块情况;
(2)在内存回收过程中,输出每一次内存回收后的结果:即空闲/占用分区块情况;
(3)在每一次对分区块进行划分/合并的时候,输出相应的划分/合并信息。
3. 实验要求:
(1)假设有 n=8 个进程,每个进程所申请的内存块大小为 2^k,其中 k 随机在[3,8]内产生。
自顶向下模块化设计
整体框架
根据题意,需要实现的算法在整体上分为Init、Buddy_system、Print
-
Init:需要实现的功能是初始化Block、PCB和Blocklist(管理不同大小块的链表的数组),故分为三个函数进行实现。
-
Buddy_system:需要实现的是伙伴系统本身,其又分为了分配和回收两个大的子函数,两个子函数又可以继续细分。
-
Print:穿插在上面几个部分之中,根据具体需要设计即可,需要用到四个Print函数,用来输出分配、回收、合并结果和区块情况。
部分具体实现
Init部分
由于实现起来相对简单,故不做对具体实现的讲解。
void Set_Block(Block* block, int id, int size, int startAddr, bool status, int pid, Block* prev, Block* next);
void Set_PCBs(PCB* pcb);
void Init_blockList(Block* blockList[]);
Buddy_system部分
主要分为两个部分:分配和回收
void Buddy_system(Block* block, PCB* pcb);
void Mem_alloc(Block* block, PCB* pcb, Block* blockList[]);
void Mem_divide(Block* blockList[], int idx);
void Mem_recycle(PCB* pcb, Block* blockList[]);
void Mem_combine(Block* blockList[], Block* block);
bool isBuddy(Block* block, Block* buddy);
Buddy_system:作为最高层的抽象,进行分配和回收以及一些预备性的操作。
void Buddy_system(Block* block, PCB* pcb) {
Block* blockList[maxBlockList]; //maxK + 1组伙伴系统管理的链表
Init_blockList(blockList);
blockList[maxBlockList - 1] = block; //序号maxK的元素对于大小为maxK的块
Mem_alloc(block, pcb, blockList);
Mem_recycle(pcb, blockList);
return;
}
Mem_alloc:抽象出整个把内存分配给PCB的过程,把最复杂的划分功能进一步抽象成一个子函数。
void Mem_alloc(Block* block, PCB* pcb,Block* blockList[]) {
Block* temp = NULL;
int count = 0;
bool isalloc = false;
while (pcb) {
//当PCB存在
for (int i = 0; i < maxBlockList; i++) {
if (blockList[i] && pcb->neededMem <= blockList[i]->size) {
//此链表头存在且它的SIZE够大
temp = blockList[i];
while (temp && !temp->status) {
//被占用则继续搜索
temp = temp->next;
}
if (temp) {
//如果存在这一块
isalloc = true; //说明肯定能分配
while (pcb->neededMem < temp->size) {
//如果此块大于PCB需求量
count++;
Mem_divide(blockList, i - count + 1); //对序号i - count + 1的块进行切分
temp = blockList[i - count]; //temp指向序号i - count的块
while (temp && !temp->status) {
//被占用则继续搜索
temp = temp->next;
}
}
temp->status = false; //这一块分配给PCB
temp->pid = pcb->pid;
pcb->blockID = temp->id;
pcb->status = 1;
}
}
if(isalloc) break;
}
Print_alloc(pcb, isalloc);
Print_blockmsg(blockList);
pcb = pcb->next; //重置临时变量+指向下一个PCB
isalloc = false;
count = 0;
}
return;
}
Mem_divide:一个块给劈成两块,放入管理相应块大小的链表尾部,其他的交给上一层的函数就行;注意把原块释放掉,还有原块所在链表需要妥善处理,具体见代码。
void Mem_divide(Block* blockList[], int idx) {
Block* temp_1 = blockList[idx];
Block* temp_2 = blockList[idx - 1];
Block* create_1 = NULL;
Block* create_2 = NULL;
while (temp_1 && !temp_1->status) {
//被占用则继续搜索,这个必然找得到,只是为了定位才搜索的
temp_1 = temp_1->next;
}
if (temp_1->prev) temp_1->prev->next = temp_1->next;
if (temp_1->next) temp_1->next->prev = temp_1->prev;
while (temp_2 && temp_2->next) {
//temp和temp->next都存在,则往后移得到链表表尾
temp_2 = temp_2->next;
}
create_1 = (Block*)malloc(sizeof(Block));<