【自顶向下模块化编程】C语言实现伙伴系统

伙伴系统(Buddy system)

  1. 伙伴系统是内核中用来管理物理内存的一种算法,我们知道内存中有一些是被内核代码占用,还有一些是被特殊用途所保留,那么剩余的空闲内存都会交给内核内存管理系统来进行统一管理和分配,内核中会把内存按照页来组织分配,随着进程的对内存的申请和释放,系统的内存会不断的区域碎片化,到最后会发现,明明系统还有很多空闲内存,却无法分配出一块连续的内存,这对于系统来说并不是好事。而伙伴系统算法就是为了缓解这种碎片化。

  2. 伙伴系统(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]内产生。

自顶向下模块化设计

整体框架

根据题意,需要实现的算法在整体上分为InitBuddy_systemPrint

  1. Init:需要实现的功能是初始化BlockPCBBlocklist(管理不同大小块的链表的数组),故分为三个函数进行实现。

  2. Buddy_system:需要实现的是伙伴系统本身,其又分为了分配回收两个大的子函数,两个子函数又可以继续细分。

  3. 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));<
假设系统的可利用空间容量为2m个字,则系统开始运行时,整个内存区是一个大小为2m的空闲分区。在系统运行过程中,由于不断的划分,可能会形成若干个不连续的空闲分区,将这些空闲分区根据分区的大小进行分类,对于每一类具有相同大小的所有空闲分区,单独设立一个空闲分区双向链表。这样,不同大小的空闲分区形成了k(0≤k≤m)个空闲分区链表。 当需要为进程分配一个长度为n的存储空间时,首先计算一个i值,使2i-1<n≤2i,然后在空闲分区大小为2i的空闲分区链表中查找。若找到,即把该空闲分区分配给进程。否则,表明长度为2i的空闲分区已经耗尽,则在分区大小为2i+1的空闲分区链表中寻找。若存在2i+1的一个空闲分区,则把该空闲分区分为相等的连个分区,这两个分区称为一对伙伴,其中的一个分区用于分配,而把另一个加入分区大小为2i的空闲分区链表中。若大小为2i+1的空闲分区不存在,则需要查找大小为2i+2的空闲分区,若找到则对其进行两次分割:第一次,将其分割为大小为2i+1的两个分区,一个用于分配,一个加入到大小为2i+1空闲分区链表中;第二次,将第一次用于分配的空闲分区分割为2i的两个分区,一个用于分配,一个加入到大小为2i空闲分区链表中。若仍然找不到,则继续查找大小为2i+3的空闲分区,以此类推。由此可见,在最坏的情况下,可能需要对2k的空闲分区进行k次分割才能得到所需分区。 与一次分配可能要进行多次分割一样,一次回收也可能要进行多次合并,如回收大小为2i的空闲分区时,若事先已存在2i的空闲分区时,则应将其与伙伴分区合并为大小为2i+1的空闲分区,若事先已存在2i+1的空闲分区时,又应继续与其伙伴分区合并为大小为2i+2的空闲分区,依此类推。 2.2 伙伴系统的需求 根据伙伴系统算法的思想,我们组对本系统的功能划分为3种: ⑴ 根据伙伴系统算法分配内存 ⑵ 根据伙伴系统算法回收内存 ⑶ 实时查看内存使用的情况
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值