前言

学到了操作系统的的虚拟内存部分,硬件不太好的我学起来有些吃力,概念性知识点太多,所以我决定用软件的方式,实现一下虚拟内存常用的算法,因为用到了指针,暂时用C语言写一下Buddy算法、FIFO算法、LRU算法、Clock算法。我知道其实图形化的算法展示更加直观和容易理解,但是有些惭愧,JavaFX只是学了入门,动画3D之类的还没深入学,只能用自己的方式去实现,不过。目的主要是利用他们的思想。虚拟内存的知识会简单的总结一下,但是不会详细展开,因为我自己也不是很理解,只是先实现一下算法。

本博客实现一下Buddy算法

Buddy算法:操作系统学习之用C语言模拟伙伴(Buddy)算法
FIFO算法:操作系统学习之用C语言模拟FIFO算法
LRU算法:操作系统学习之用C语言模拟LRU算法
Clock算法:操作系统学习之用C语言模拟CLOCK算法

本源代码原创,转载请注明,同时由于本人才疏学浅,刚入门操作系统,如有错误敬请指教
本文原创,创作不易,转载请注明!!!
本文链接
个人博客:https://ronglin.fun/?p=197
PDF链接:见博客网站
CSDN: https://blog.csdn.net/RongLin02/article/details/117340021

概念

在内存管理中,有动态分区方案和固定分区方案,但都存在一定的缺陷。固定分区方案限制了活动进程的数量,且若可用的分区大小与进程大小很不匹配,则内存空间的利用率就会非常低。动态分区的维护特别复杂,并且会引入进行压缩的额外开销。于是一个折中的方案提出–伙伴系统

理论部分虽然十分头疼,但是一定要仔仔细细的看。

教科书的解释

操作系统-精髓与设计原理(第九版)的解释
伙伴系统中可用内存块的大小为2k个字,L≤K≤U,其中2L表示分配的最小快的尺寸,2U表示分配的最大快的尺寸,通常2U是可供分配的整个内存的大小。
最初,可用于分配的整个空间被视为一个大小块为2U的块。若请求的大小s满足2U-1 ≤ s ≤ 2U,则分配整个空间。否则,该块分成两个大小相等的伙伴,大小均为2U-1。若有2U-2 ≤ s ≤ 2U-1,则给该请求分配两个伙伴中的任何一个;否则,其中的一个伙伴又被分成两半,持续这一过程,直到产生大于等于s的最小快,并且分配给该请求。在任何时候,伙伴系统中为所有大小为2i的“空洞”维护一个列表。空洞可通过对半分裂从i+1列表中移出,并且在i列表中产生两个大小为2i的伙伴。当i列表中的一对伙伴都变成未分配的块时,将他们从i列表中移出,合并为i+1列表中的一个块。

理解

文字好多,看着有点吃力。其实仔细一分析,这个伙伴系统和一个名为“4096”的游戏很像,请求时,找一个“合适”的盒子放进程,当释放的时候就像游戏的那样,相同大小的块合并。
不过,伙伴系统实际来说,用一个二叉树表示更为合适.
我这里用一个链表数组来维护,
这里有个资料可以看看内存管理算法–Buddy伙伴算法

算法模拟

不多bb,开始头秃

源代码

本源代码原创,转载请注明,同时由于本人才疏学浅,刚入门操作系统,如有错误敬请指教

#include<stdio.h>
#include<stdlib.h>
#include <time.h>
#define MAX_SIZE 1024
#define MAX_NUM_PROC 10
#define MAX_PROC_SIZE 100//进程结构体
//state 阻塞/初始-1 就绪0 运行1
struct Process
{int pid;int ppid;int state;int p_size;void init(){pid = -1;ppid = -1;state = -1;p_size = 0;}
}procs[MAX_NUM_PROC];//buddy中的每一块的结构
struct Block
{struct Process proc;int use;Block* next;void init(){proc.init();use = 0;next = NULL;}
};//buddy链表的头
struct Buddy
{int all_size;Block* next;void init(){all_size = MAX_SIZE;next = NULL;}
};void initBuddy(struct Buddy* L,int list_num);//初始化buddy队列
void printState(struct Buddy* L,int list_num);//打印当前buddy队列的状态
int dealProcess(struct Buddy* L,int index,struct Process proc);//处理进程的请求
void SplitBlock(struct Buddy* L,int index);//将上一级的块分裂成两个当前块int main()
{//初始化int list_num = 0;for(int mi = MAX_SIZE;mi>0;mi/=2)list_num++;//printf("list_num = %d\n",list_num); list_num = 11;struct Buddy buddy[list_num];initBuddy(buddy,list_num);printState(buddy,list_num);//生成n个进程,每一个进程所需空间随机for(int i=0;i<MAX_NUM_PROC;i++){srand((unsigned)time(NULL)*i);procs[i].init();procs[i].pid=i+1;procs[i].ppid=0;procs[i].p_size = rand() % MAX_PROC_SIZE +1; //随机数范围(0,MAX_PROC_SIZE];}//现在开始实现分配printf("\n现在开始实现分配\n");for(int i=0;i<MAX_NUM_PROC;i++){printf("\npid = %d,state = %d,p_size=%d\n",procs[i].pid,procs[i].state,procs[i].p_size);if(procs[i].p_size ==0){printf("此进程所需大小为0,不需要分配空间\n");continue;}int res=dealProcess(buddy,list_num-1,procs[i]);//int res=0;if(res){printf("分配成功,分配块大小:%d\n",res);procs[i].state=1;}else{printf("分配失败\n");}}printState(buddy,list_num);return 0;
}void initBuddy(struct Buddy* L,int list_num)
{int size_two=1;struct Block* first_block =(struct Block*)malloc(sizeof(struct Block));first_block->init();for(int i=0;i<list_num;i++){L[i].init();L[i].all_size = size_two;if(i ==10)//设置默认的最大块{L[i].next=first_block;}size_two *= 2;}
}void printState(struct Buddy* L,int list_num)
{for(int i=0;i<list_num;i++){printf("size=%d: ",L[i].all_size);struct Block* p=L[i].next;while(p){if(p->use){printf("pid=%d",p->proc.pid);}else{printf("use=%d",p->use);}printf(";");p=p->next;}printf("\n");}}int dealProcess(struct Buddy* L,int index,struct Process proc)
{if(index <= -1 || L[index].all_size<proc.p_size)return 0;if(index==0 || L[index-1].all_size < proc.p_size )//当前的块就是要找的块{int flag = 0;struct Block* p = L[index].next;while(p){if(p->use==0){flag=1;p->proc=proc;p->use=1;return L[index].all_size;}p=p->next;}if(flag==0){SplitBlock(L,index);//分裂大块//现在寻找执行完分裂之后是否存在可用的块p = L[index].next;while(p){if(p->use==0){flag=1;p->proc=proc;p->use=1;return L[index].all_size;}p=p->next;}if(flag==0)return 0;}}else{return dealProcess(L,index-1,proc);}
}void SplitBlock(struct Buddy* L,int index)
{if(L[index].all_size== MAX_SIZE)return ;int flag=0;struct Block* p = L[index+1].next;struct Block* q = NULL;while(p)//寻找空闲的块{if(p->use == 0){flag=1;break;}p=p->next;}if(flag ==0){SplitBlock(L,index+1);}p = L[index+1].next;while(p)//寻找空闲的块{if(p->use == 0)//找到了,大块分裂成两个本块{if(q==NULL)//说明p指向的块是头节点之后的那个{L[index+1].next=p->next;}else{q->next=p->next;}free(p);//尾插法插入两个新块struct Block* t1 =(struct Block*)malloc(sizeof(struct Block));struct Block* t2 =(struct Block*)malloc(sizeof(struct Block));t1->init();t2->init();t1->next=t2;if(L[index].next==NULL){L[index].next=t1;}else{struct Block* t = L[index].next;while(t->next)t=t->next;t->next=t1;}break;}q=p;p=p->next;}}

运行结果

size=1:
size=2:
size=4:
size=8:
size=16:
size=32:
size=64:
size=128:
size=256:
size=512:
size=1024: use=0;现在开始实现分配pid = 1,state = -1,p_size=39
分配成功,分配块大小:64pid = 2,state = -1,p_size=2
分配成功,分配块大小:2pid = 3,state = -1,p_size=66
分配成功,分配块大小:128pid = 4,state = -1,p_size=29
分配成功,分配块大小:32pid = 5,state = -1,p_size=92
分配成功,分配块大小:128pid = 6,state = -1,p_size=56
分配成功,分配块大小:64pid = 7,state = -1,p_size=19
分配成功,分配块大小:32pid = 8,state = -1,p_size=82
分配成功,分配块大小:128pid = 9,state = -1,p_size=45
分配成功,分配块大小:64pid = 10,state = -1,p_size=9
分配成功,分配块大小:16
size=1:
size=2: pid=2;use=0;
size=4: use=0;
size=8: use=0;
size=16: pid=10;
size=32: pid=4;pid=7;use=0;
size=64: pid=1;pid=6;pid=9;use=0;
size=128: pid=3;pid=5;pid=8;
size=256: use=0;
size=512:
size=1024:

结果的图示:

代码缺点

先说这个模拟的缺点,缺点是:只是维护了链表数组,并没有维护"伙伴"这个性质,仅仅是进程有需求,就按照buddy分配,没有维护哪两个块是伙伴,也就是说只体现了分配,没体现伙伴。所以没写释放进程之后的块合并过程,因为不知道哪两个块是伙伴,要想解决也简单,就是在Block结构体中,再加一个属性,用来表示"伙伴",然后分裂和合并的时候,根据这个属性确定自己的"伙伴"。
实现匆忙,再加上本人理论不扎实,有好想法可告诉我。

解释代码

代码250多行,解释起来有点麻烦,简单的解释一下。
先说输出
use=0表示这个块被切割了,但是还没用,如果被用了,就输出是哪个进程占用。可以对照上图查看。
结构体
进程结构体
这里边包含了一些一个进程的基本信息,因为不涉及到硬件,只是纯软件模拟,比PCB中的属性少了很多,然后定义是在全局中定义了MAX_NUM_PROC个进程,每个进程的初始化是在主函数的for循环中,用了一个随机函数,生成进程的所需空间,随机数范围是(0,MAX_PROC_SIZE],可根据自己的需要修改。
Buddy结构体
这个是每个链表的头,数据域提供了这个链表的表示的大小,比如表示的512、64等。
Block结构体
每一个块的数据结构,数据域有两个一个是存放的进程,还有一个是是否被使用,这个use属性的意思是,伙伴系统分裂出来两个,一个被使用,而它的伙伴可能还没被使用。
主函数
主函数的工作比较简单,就是初始化buddy链表,生成MAX_NUM_PROC个进程,然后按照buddy算法分配内存给进程。
子函数

void initBuddy(struct Buddy* L,int list_num);//初始化buddy队列
void printState(struct Buddy* L,int list_num);//打印当前buddy队列的状态
int dealProcess(struct Buddy* L,int index,struct Process proc);//处理进程的请求
void SplitBlock(struct Buddy* L,int index);//将上一级的块分裂成两个当前块

一共四个子函数,第一个和第二个是用来描述buddy链表,难点是后两个递归函数的实现过程。
dealProcess()函数是递归寻找 对于 进程 “适合"大小的块,然后将块分给进程。如果没找到这个块,就会调用SplitBlock()函数,将上层的大块,依次递归分裂生成小块,直到本层,如果分裂完,还没有适合的块,就返回错误,会输出"分配失败”。

总结

尝试过用面向对象的写法,最开始用Java写,结果发现由于Java的指针机制,链表的实现太麻烦,后来又用了C++的面向对象,发现需要太多的引用和指针,维护链表着实有点大材小用,而且有点麻烦,最后决定用面向过程的思维写,既然是面向过程,最后决定用C语言实现,buddy算法思维容易理解,可真正实现的时候发现了很多很多问题。这些算法真的要自己敲一遍才知道它真正的意义,获益匪浅。
=w=

操作系统学习之用C语言模拟伙伴(Buddy)算法相关推荐

  1. 操作系统学习之用C语言模拟CLOCK算法

    前言 CLOCK算法,顾名思义,时钟算法,是一个在FIFO和LRU的折衷算法,很符合我们的中庸之道,来学一学它折衷了哪些部分. Buddy算法:操作系统学习之用C语言模拟伙伴(Buddy)算法 FIF ...

  2. 操作系统学习之用C语言模拟LRU算法

    前言 LRU算比较经典,而且考的也比较多,LRU算法全称Least Recently Used,译为最近最少使用.用C模拟一下吧. Buddy算法:操作系统学习之用C语言模拟伙伴(Buddy)算法 F ...

  3. 操作系统学习之用C语言模拟FIFO算法

    前言 FIFO算法比较简单,没什么好说的,就是先进先出.不过我添加了3状态,不过也只有堵塞,没有将阻塞进程唤醒的过程. Buddy算法:操作系统学习之用C语言模拟伙伴(Buddy)算法 FIFO算法: ...

  4. 内存算法-伙伴(buddy)算法

    伙伴(buddy)算法,它不能根据需要从被管理内存的开头部分创建新内存.它有明确的共性,就是各个内存块可分可合,但不是任意的分与合.每个块都有个朋友,或叫"伙伴",既可与之分开,又 ...

  5. 操作系统C语言模拟内存分配算法的模拟实现

    使用一个一维数组来模拟内存储空间,建立内存块来记录内存分配使用情况,通过随机产生进程及其所需要的内存来模拟真实的进程.通过给进程分配内存及回收来实现对动态不等长存储管理方法. 代码 #include ...

  6. c语言测试时间片大小,C语言模拟实现时间片轮转算法和优先级调度算法

    一.目的和要求 进程调度是处理机管理的核心内容.本实验要求用高级语言编写模拟进程调度程序,以便加深理解有关进程控制快.进程队列等概念,并体会和了解优先数算法和时间片轮转算法的具体实施办法. 二.实验内 ...

  7. 操作系统实验六:作业调度算法模拟

    一.实验目的 (1)掌握周转时间.等待时间.平均周转时间等概念及其计算方法. (2)理解五种常用的进程调度算法(FCFS.SJF.HRRF.HPF.RR),区分算法之间的差异性,并用C语言模拟实现各算 ...

  8. java语言模拟_Java语言模拟操作系统.doc

    河北大学2010级操作系统课程设计论文 PAGE PAGE 27 装订线 装 订 线 (指导教师用表) 学 生 姓 名 指 导 教 师 论文(设计)题目 Java语言模拟操作系统 主要研究 (设计)内 ...

  9. 操作系统大作业 基于Linux的模拟进程调度算法 运用c++语言编程 在VMware虚拟机里 centos 亲自写亲自测试 代码 说明书

    发布文章 博文管理我的博客退出 Trash Temp 操作系统大作业 基于Linux的模拟进程调度算法 运用c++语言编程 在VMware虚拟机里 centos 亲自写亲自测试 代码 说明书 @[TO ...

最新文章

  1. 2021年大数据Flink(二十五):Flink 状态管理
  2. php mysql 星级评分_jQuery+PHP星级评分实现方法_jquery
  3. Sublime Text 快捷键
  4. 【AWS】DynamoDB扫描操作获取表全部数据
  5. 批量添加自定义用户控制,界面闪烁解决方案
  6. JNI方面的笔记(未完待续)
  7. mongodb查询文件服务器的数据,服务器端知识库mongodb基础篇
  8. Open3d之点云离群点剔除
  9. cPanel附加域名出现Error from park wrapper: 使用带以下 IP 的命名服务器:
  10. CLin 和 IDEA创建文件时自动添加作者和时间信息 + 多个.cpp文件在CLion下运行问题...
  11. react 点击使父元素消失_在 React 组件中使用 Refs 指南
  12. 极大似然法(ML)与最大期望法(EM)
  13. 高校固定资产折旧使用计算机,高校固定资产管理系统功能介绍
  14. docker打包镜像--go语言编写的http项目
  15. Python与数据库之学员管理系统
  16. 【可穿戴技术】相关资料
  17. Android 活用RecyclerView分割线
  18. OVP过压保护IC高耐压36V,首选钰泰ETA7008/ETA7014
  19. 【2022研电赛】商业计划书赛道华南区二等奖:基于机器视觉的智能驾驶辅助系统
  20. OSChina 周日乱弹 —— 请务必让我分担他们的痛苦!

热门文章

  1. VMware10安装centos6.5(64位)
  2. java 原子量Atomic举例(AtomicReference)
  3. c#调用本地命令并截取Output
  4. rhel5 给grub 加密,亲测!
  5. 实现灵活的IT架构的三个要点
  6. 优化了破网站的搜索功能
  7. 编程入门到进大厂,分享一点学习经验
  8. 零基础也能看懂!数据仓库与数据库的这几个问题,你能回答出来吗
  9. 浅析数据库设计三范式
  10. 每人都有两大炸弹的扎金花2012