自顶向下设计-伙伴系统

  • 伙伴系统(Buddy system)
  • 实验要求
  • 自顶向下模块化设计
    • 整体框架
    • 部分具体实现
      • Init部分
      • Buddy_system部分
      • Print部分
    • 整体代码实现
  • 总结及心得

伙伴系统(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;   //这一块分配给PCBtemp->pid = pcb->pid;pcb->blockID = temp->id;pcb->status = 1;}}if(isalloc) break;}Print_alloc(pcb, isalloc);Print_blockmsg(blockList);pcb = pcb->next;    //重置临时变量+指向下一个PCBisalloc = 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));   //链表表尾添加两个块create_2 = (Block*)malloc(sizeof(Block));Set_Block(create_1, temp_1->id, idx - 1, temp_1->startAddr, true, -1, temp_2, create_2);Set_Block(create_2, Block_ID + 1, idx - 1, temp_1->startAddr + pow(2, idx - 1), true, -1, create_1, NULL);if (temp_2)temp_2->next = create_1;else blockList[idx - 1] = create_1;Block_ID += 1;  //每次分块本质上只增加了1块,记一次数if (temp_1 == blockList[idx])blockList[idx] = blockList[idx]->next; //当释放掉的块为表头时,表头变为下一个块free(temp_1); //分掉,或者说释放掉return;
}

Mem_recycle:抽象出整个回收PCB内存的过程,将最为复杂的合并过程抽象成一个子函数,合并过程的子函数相对简单,注意不要把两个不同大小的块纳入合并的考虑范围就好。

void Mem_recycle(PCB* pcb, Block* blockList[]) {Block* temp = NULL;while (pcb) {if (pcb->status == 1) {for (int i = 0; i < maxBlockList; i++) {temp = blockList[i];while (temp && pcb->blockID != temp->id) temp = temp->next;if (temp) {temp->status = true;    //解除占用,暂时不释放pcb->blockID = -1;pcb->status = -1;temp->pid = -1;Print_recycle(pcb);Mem_combine(blockList, temp);   //合并并且释放,包含了输出combine信息Print_blockmsg(blockList);pcb = pcb->next;break;}}}}return;
}

Print部分

没有什么可说的,注意细节。

void Print_alloc(PCB* pcb, bool isalloc);
void Print_recycle(PCB* pcb);
void Print_combine(Block* block, Block* buddy);
void Print_blockmsg(Block* blockList[]);

整体代码实现

一如既往的,只要理清楚了整体的框架,剩下要做的就是把分解的子函数逐一实现,然后DEBUG。

#include <stdio.h>
#include <math.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>#define maxK 10     //系统初始时最大的内存容量,以2的maxK次幂来表示
#define numOfProcs 8
#define maxBlockList 11
#define memLB 3
#define memUB 8
#define FREE true
#define USED falsetypedef struct Block {  //可以作为分区链表头int id;             //分区的序号int size;           //分区的大小int startAddr;      //分区的开始位置bool status;        //分区的状态:true为空闲,false为被占用int pid;            //如果该分区被占用,则存放占用进程的id; 否则为 - 1struct Block* prev;  //指向前面一块内存分区struct Block* next;   //指向后面一块内存分区
}Block;typedef struct PCB {int pid;            //进程的序号int neededMem;      //需要的内存分区大小int status;         //1:内存分配成功;-1:分配失败int blockID;        //如果分配成功,保存占用分区的id,否则为 - 1struct PCB* next;   //指向下一个PCB
}PCB;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 Sort_PCBs(PCB* pcb);
void Init_blockList(Block* blockList[]);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);void Print_alloc(PCB* pcb, bool isalloc);
void Print_recycle(PCB* pcb);
void Print_combine(Block* block, Block* buddy);
void Print_blockmsg(Block* blockList[]);int Block_ID = 0;   //全局变量方便记录id号,次序根据创建时间排int main() {srand((unsigned)time(NULL));    //刷新随机数种子Block* block = (Block*)malloc(sizeof(Block));PCB* pcb = (PCB*)malloc(sizeof(PCB));Set_Block(block, Block_ID, maxK, 0, FREE, -1, NULL, NULL);Set_PCBs(pcb);Buddy_system(block, pcb);return 0;
}void Set_Block(Block* block, int id, int size, int startAddr, bool status, int pid, Block* prev, Block* next) {block->id = id;block->next = next;block->pid = pid;block->prev = prev;block->size = size;block->startAddr = startAddr;block->status = status;return;
}void Set_PCBs(PCB* pcb) {PCB* temp = pcb;for (int i = 0; i < numOfProcs; i++) {temp->pid = i;temp->neededMem = memLB + rand() % (memUB - memLB + 1);temp->status = -1;temp->blockID = -1;if (i != numOfProcs - 1) {temp->next = (PCB*)malloc(sizeof(PCB));temp = temp->next;continue;}temp->next = NULL;}Sort_PCBs(pcb); //不是必要的return;
}void Sort_PCBs(PCB* pcb) {PCB* p = NULL;PCB temp = {};bool isChange = true;if (pcb == NULL || pcb->next == NULL) return;while (p != pcb->next && isChange) {PCB* q = pcb;isChange = false;for (; q->next && q->next != p; q = q->next) {if (q->neededMem > q->next->neededMem) {temp = *q;q->neededMem = q->next->neededMem;q->pid = q->next->pid;q->next->pid = temp.pid;q->next->neededMem = temp.neededMem;isChange = true;}}p = q;}return;
}void Init_blockList(Block* blockList[]) {for (int i = 0; i < maxBlockList; i++) {blockList[i] = NULL;}return;
}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;
}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;   //这一块分配给PCBtemp->pid = pcb->pid;pcb->blockID = temp->id;pcb->status = 1;}}if(isalloc) break;}Print_alloc(pcb, isalloc);Print_blockmsg(blockList);pcb = pcb->next;    //重置临时变量+指向下一个PCBisalloc = false;count = 0;}return;
}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));   //链表表尾添加两个块create_2 = (Block*)malloc(sizeof(Block));Set_Block(create_1, temp_1->id, idx - 1, temp_1->startAddr, true, -1, temp_2, create_2);Set_Block(create_2, Block_ID + 1, idx - 1, temp_1->startAddr + pow(2, idx - 1), true, -1, create_1, NULL);if (temp_2)temp_2->next = create_1;else blockList[idx - 1] = create_1;Block_ID += 1;  //每次分块本质上只增加了1块,记一次数if (temp_1 == blockList[idx])blockList[idx] = blockList[idx]->next; //当释放掉的块为表头时,表头变为下一个块free(temp_1); //分掉,或者说释放掉return;
}void Mem_recycle(PCB* pcb, Block* blockList[]) {Block* temp = NULL;while (pcb) {if (pcb->status == 1) {for (int i = 0; i < maxBlockList; i++) {temp = blockList[i];while (temp && pcb->blockID != temp->id) temp = temp->next;if (temp) {temp->status = true;    //解除占用,暂时不释放pcb->blockID = -1;pcb->status = -1;temp->pid = -1;Print_recycle(pcb);Mem_combine(blockList, temp);   //合并并且释放,包含了输出combine信息Print_blockmsg(blockList);pcb = pcb->next;break;}}}}return;
}void Mem_combine(Block* blockList[], Block* block) {    //需要说明,连环合并只需要顺序执行即可Block* temp = NULL;Block* transfer = NULL;for (int i = 0; i < maxBlockList; i++) {if (block->size != i) continue;     //如果不同尺寸就别合并temp = blockList[i];while (temp && !isBuddy(block, temp)) {temp = temp->next;}if (temp) {if (block->startAddr > temp->startAddr) block->startAddr = temp->startAddr; //把temp融入blockPrint_combine(block, temp);block->size += 1;if (block->prev) block->prev->next = block->next;   //合并后的块移动到下一链表中(变大了一倍),一定要注意如果block就是表头的情况,不处理就会多出来if (block->next) block->next->prev = block->prev;if (block == blockList[i]) blockList[i] = blockList[i]->next;transfer = blockList[i + 1];while (transfer && transfer->next) transfer = transfer->next;   //定位到队尾if (transfer) {block->next = transfer->next;block->prev = transfer;transfer->next = block;}else {blockList[i + 1] = block;block->next = NULL;block->prev = NULL;}if (temp->prev) temp->prev->next = temp->next;  //正式释放此“兄弟块”if (temp->next) temp->next->prev = temp->prev;if (temp == blockList[i]) blockList[i] = blockList[i]->next;free(temp);}}return;
}bool isBuddy(Block* block, Block* buddy) {int buddyAddr = 0;int size = pow(2, block->size);if (block->startAddr % (size * 2) == 0) {buddyAddr = block->startAddr + size;}else if (block->startAddr % (size * 2) == size) {buddyAddr = block->startAddr - size;}if (buddy->startAddr == buddyAddr && buddy->status) return true;    //不被占用且地址相邻else return false;
}void Print_alloc(PCB* pcb,bool isalloc) {if (isalloc)printf("Allocate free memory block for process #%d of size 2^%d…\n", pcb->pid, pcb->neededMem);else printf("Fail to allocate free memory block for process #%d of size 2^%d…\n", pcb->pid, pcb->neededMem);return;
}void Print_recycle(PCB* pcb) {printf("Recycle used memory block for process #%2d of size 2^%d…\n", pcb->pid, pcb->neededMem);return;
}void Print_combine(Block* block, Block* buddy) {printf("Combine block %d and %2d of size 2^%d\n", block->id, buddy->id, block->size);return;
}void Print_blockmsg(Block* blockList[]) {Block* temp = NULL;for (int i = 0; i < maxBlockList; i++) {temp = blockList[i];while (temp) {if (temp->status)printf("free block id:%2d, size:2^%d, startAddr:%4d\n", temp->id, temp->size, temp->startAddr);temp = temp->next;}}for (int i = 0; i < maxBlockList; i++) {temp = blockList[i];while (temp) {if (!temp->status)printf("used block id:%2d, size:2^%d, startAddr:%4d, pid:%d\n", temp->id, temp->size, temp->startAddr,temp->pid);temp = temp->next;}}printf("\n");return;
}

结果展示


总结及心得

  1. 双向链表的相关操作还是不太熟练,导致涉及这一块的函数经常出BUG,也许我应该抽出点时间来复习一下数据结构了。

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

  1. 【自顶向下模块化编程】C语言实现多级反馈队列调度算法

    自顶向下-多级反馈队列 多级反馈队列算法 算法原理 算法描述 题目摘要 自顶向下模块化设计 整体框架 具体实现 Generator Scheduler Executor 整体代码实现 总结及心得 总结 ...

  2. keil c语言模块化编程,keil C模块化编程总结

    昨晚看了下模块化编程的东西,把自己的工程整了整,可惜没成功.今早发神经似的起床敲代码,很快就发现了错误,原来是条件宏定义的头文件名忘改了,汗!!! 整理下模块化编程的要点,感谢以下三位UP主的帖子: ...

  3. C语言模块化编程的例子

    以往写C语言程序都是一个文件里面写个几十.几百行,在练算法的时候还可以,现在搞开发需要模块化编程,所谓模块化编程,就是指一个程序包含多个源文件(.c 文件和 .h 文件),每个 .c 文件可以被称为一 ...

  4. c++头文件被c语言调用需要注意什么_嵌入式C语言之模块化编程

    C语言中的模块化体现在两个方面: 1 函数. 函数是C语言的最小单位,每个函数均实现一个独立的功能,于是每个函数均可以当做是一个最小的功能模块.这样,C语言就实现了最基本的模块化. 2 文件. 在C语 ...

  5. C语言探索之旅 | 第二部分第一课:模块化编程

    C语言之父 Dennis Ritchie -- 简书作者 谢恩铭 转载请注明出处 第二部分第一课:模块化编程 上一课是C语言探索之旅 | 第一部分第十课:练习题+习作,至此,我们[C语言探索之旅]的第 ...

  6. C语言模块化编程样例

    模块化编程向来不是面向对象语言的专利,即使是C语言,为了降低文件.模块间的耦合度,依然要注意对变量.函数进行封装. 以下举例对C语言模块化编程进行浅析:项目中包含a.c和b.c文件,其中a.c中定义了 ...

  7. C语言怎么进行编程大型项目,如何对一个大的项目进行模块化编程

    当你在一个项目小组做一个相对较复杂的工程时,意味着你不再独自单干.你需要和你的小组成员分工合作,一起完成项目,这就要求小组成员各自负责一部分工程.比如你可能只是负责通讯或者显示这一块.这个时候,你就应 ...

  8. c语言模块化编程extern的用法,关于模块化编程extern用法

    单片机模块化 用C语言编写程序的时候,我们经常会遇到这样一种情况:希望在头文件中定义一个全局变量,然后包含到两个不同的c文件中,希望这个全局变量能在两个文件中共用. 举例说明:项目文件夹project ...

  9. 嵌入式C语言之---模块化编程

    当你在一个项目小组做一个相对较复杂的工程时,意味着你不再独自单干.你需要和你的小组成员分工合作,一起完成项目,这就要求小组成员各自负责一部分工程.比如你可能只是负责通讯或者显示这一块.这个时候,你就应 ...

最新文章

  1. SpringBatch配置数据库
  2. windowsphone开发_[app开发定制公司]开发app需要什么技术呢?
  3. js-数组方法的使用和详谈
  4. WebSocket原理及使用场景(转载)
  5. 本地开发时连接后台数据库时出现的错误,附自救方法
  6. MUI 拍照和从系统相册选择图片上传
  7. linux下文件时间戳
  8. 在CentOS 5.5安装 Apache2 和 PHP5 及 MySQL
  9. C语言实战--DLL注入器
  10. 18-HTML标签的居中
  11. 【渝粤题库】陕西师范大学 《道德教育案例研究》作业
  12. 在腾讯,有多少技术Leader在写代码?
  13. 基于SSM(Spring+SpringMVC+MyBatis)的外卖点餐管理系统
  14. 保研面试/考研复试高等数学问题整理
  15. DFS 003:棋盘问题
  16. 算法——归并和归并排序
  17. python怎么播放视频教程_python怎样播放视频?
  18. fbm是什么意思_fba是什么意思
  19. windows下,对opencv进行gcc/g++编译
  20. 【MySQL开启密码复杂度】

热门文章

  1. first blogs C语言
  2. 双十一临近,京东出奇招,摆摊卖娃娃
  3. 2066 Problem B 分组统计
  4. 初中学历可以报考成人大专吗?
  5. 用PS将一张暗照片变亮
  6. 图论---Hamilton圈
  7. 2014年电大计算机网考报名,电大2014年网络统考计算机网考操作题提示(已排版)...
  8. 通过getpid()函数,获取进程标识符pid
  9. jsp和js区别 一
  10. matlab中设置拟合初值,matlab中的nlinfit函数时,参数需要回归系数的初值,如何确定?...