1. 基本信息
我完成了两个内存分配算法:
-
伙伴系统,
-
分页内存分配算法
2. 记录内存空间使用情况
伙伴系统
-
使用结构体来记录内存空间使用情况
-
id为内存块编号。
-
start为内存块起始地址。
-
size为内存块大小。
-
isFree为内存块使用情况,true表示被进程使用,false表示没被进程使用。
-
pid为占用此内存块的进程号,-1表示没进程使用。
-
next指向下一个内存块。
typedef struct MemoryBlock{
int id;
int start;
int size;
bool isFree;
int pid;
struct MemoryBlock *next;
}MemoryBlock;
-
使用链表存储内存空间队列,空闲分区和非空闲分区存储在同一个链表。 初始化内存块
-
第一块内存块id为1
-
内存起始地址为0
-
内存大小为1024
-
内存使用情况为空闲
-
pid为-1,表示没有进程使用
-
next指向NULL表示没有下一个内存块
void initMemoryBlock(){
head = (MemoryBlock*)malloc(sizeof(MemoryBlock));
head->id=1;
head->start=0;
head->size=1024;
head->isFree=false;
head->pid=-1;
head->next=NULL;
}
-
使用循环遍历printf打印内存块使用情况。
-
打印时区分两种情况,空闲内存块和非空闲内存块。
-
打印顺序按内存块起始地址排序。
void printMemoryBlock(){
MemoryBlock *p=head;
printf("当前内存占用情况\n");
while(p!=NULL){
if(p->isFree==false){
printf("内存块号:%d 起始地址:%d 内存块大小:%d 空闲状态:空闲 占用进程号:%d\n ",p->id,p->start,p->size,p->pid);
}else{
printf("内存块号:%d 起始地址:%d 内存块大小:%d 空闲状态:占有 占用进程号:%d\n ",p->id,p->start,p->size,p->pid);}
p=p->next;
}
}
分页内存分配
-
使用数据结构来记录内存的使用,结构体创建类似下图的页表
typedef struct PTABLE{
int pid;
int page_num;
int frame_num;
}PTABLE;
-
由于是分页内存分配系统,我设置一个页框大小为4,进程大小超过4的就会被拆分成多个页面,并记录在结构体中。
-
pid为进程编号。
-
page_num为当前进程的页号。
-
frame_num为物理页号。
-
使用此页表来记录所有内存使用的情况
PTABLE page_table[NUM_PAGE];
-
查询内存使用情况时,遍历页表,再根据页表中逻辑地址打印出实际物理地址,展示内存使用情况。
void print_memory(){
for(int i=0;i<NUM_PAGE;i++){
if(page_table[i].pid!=0){
printf("这是%d号进程的第%d页,逻辑地址是%d,物理地址是%d\n",page_table[i].pid,page_table[i].page_num+1,page_table[i].frame_num/4,page_table[i].frame_num);
}
}
}
3. 记录空闲分区
伙伴系统
-
记录内存空闲分区的方法与记录内存使用情况的方法一致,存储在同一个链表中。
-
区别是否为空闲分区可以判断内存块的isFree是否等于true或pid是否等于-1。 以示意图展示存储方式:
分页内存分配
int page[NUM_PAGE]={0};
-
使用此位图来记录空闲分区,如果位图中的值为0,相应地址就为空闲分区。
-
原理如下图:
4. 内存分配算法
伙伴系统
-
内存分配:伙伴系统
-
在正式讲解内存分析算法前,先介绍下findBest函数,功能是遍历内存块链表,并找到第一个最小的且能装下进程的内存块。
MemoryBlock *findBest(int psize){
MemoryBlock *best=NULL;
MemoryBlock *p=head;
while(p!=NULL){
if(p->isFree==false&&p->size>=psize&&(best==NULL||p->size<best->size)){
best=p;
}
p=p->next;
}
return best;
}
-
mergeMemoryBlocks函数遍历内存块链表,如果发现当前内存块和下一个相邻内存块都是空闲的且为空就将两个空间合并在一起,形成一个较大的空间。
void mergeMemoryBlocks(){
MemoryBlock *p = head;
while(p!=NULL && p->next!=NULL){
if(p->isFree==false&&p->next->isFree==false){
p->size+=p->next->size;
MemoryBlock *q = p->next;
p->next=p->next->next;
free(q);
}
p=p->next;
}
printf("合并完成\n");
}
源代码:
void allocateMemory(int pid,int psize){
MemoryBlock *q=NULL;
MemoryBlock *best=findBest(psize);
if(best==NULL){
mergeMemoryBlocks();
MemoryBlock *best1=findBest(psize);
if(best1==NULL){
printf("没有适合的内存块\n");
return;
}else{
best=best1;
}
}
while(best->size>psize){
q=(MemoryBlock*)malloc(sizeof(MemoryBlock));
q->size=best->size/2;
q->isFree=false;
q->start=best->start+q->size;
q->pid=-1;
q->next=best->next;
q->id=++ID;
best->next=q;
best->size=best->size/2;
best=q;
}
best->isFree=true;
best->pid=pid;
printMemoryBlock();
printf("分配成功\n");
return;
}
源码分析:
-
首先通过findBest找到最合适的进程大小的内存块地址,若没找到合适内存块地址,就将现有的空闲内存块尝试合并出适合的大小,然后再尝试一次findBest
-
找到最合适内存块后,开始分裂内存块,知道分裂出和进程大小相匹配的内存块再停止,最后进程就放入最后一次分裂的内存块中。
-
分裂的细节,取内存块分裂出的第二块再进行分裂(即A分裂出B,C,取C分裂;C分裂出D,E,取E分裂 .......)直到分裂到合适大小为止。
-
这一段是内存块分裂的代码
-
首先申请一个新的内存块
-
然后设置这个新内存块大小为大内存块的一半
-
内存起始地址为大内存块初始地址加新内存块大小
-
大内存块地址也设置成大内存块的一半
-
大内存块内存起始地址不变
-
然后对这个新内存块继续分裂
q=(MemoryBlock*)malloc(sizeof(MemoryBlock));
q->size=best->size/2;
q->isFree=false;
q->start=best->start+q->size;
q->pid=-1;
q->next=best->next;
q->id=++ID;
best->next=q;
best->size=best->size/2;
best=q;
分页内存分配
int allocate(int pid,int psize){
int num_pages = 0;
if(psize%PAGE_SIZE==0){
num_pages = psize/PAGE_SIZE;
}else{
num_pages = psize/PAGE_SIZE+1;
}
int count=0;
int count1=0;
for(int i=0;i<NUM_PAGE;i++){
if(page[i]==0){
count++;
}
}
if(count>=num_pages){
for(int i=0;i<NUM_PAGE;i++){
if(page[i]==0){
page[i]=1;
page_table[index].pid=pid;
page_table[index].page_num=count1++;
page_table[index].frame_num=i*PAGE_SIZE;
num_pages--;
index++;
if(num_pages==0){
break;
}
}
}
}else{
return -1;
}
}
源码分析:
-
首先根据进程大小,判断并计算出进程需要占几个页框
-
统计下剩下的内存大小够不够该进程使用
-
如果不够就打印分配失败,够则继续进行分配
-
遍历空闲内存空间,把分页后的进程装入,直到进程被装完,并把装完的进程记录到页表中
-
装入页表的过程中也要更新位图。
5. 内存释放算法
伙伴系统
-
通过逐个进程的释放来释放内存
-
通过传入freeMemoryBlock进程号pid逐个释放掉进程以释放内存空间 for(int i=0;i<10;i++){ int pid=i+1; freeMemoryBlock(pid); }
-
遍历内存块链表,如果当前内存块不为空且占有内存的进程号匹配,就将isFree设置为false、pid设置为-1表示没有进程占用
-
释放完,尝试合并一下空闲分区
-
每次释放完打印一下内存分区情况
void freeMemoryBlock(int pid){
MemoryBlock* p=head;
while(p!=NULL){
if(p->isFree==true&&pid==p->pid){
p->isFree=false;
p->pid=-1;
mergeMemoryBlocks();
printMemoryBlock();
printf("释放成功\n");
}
p=p->next;
}
}
分页内存管理
int free_memory(int pid){
for(int i=0;i<NUM_PAGE;i++){
if(page_table[i].pid==pid){
page[i]=0;
page_table[i].pid=0;
page_table[i].page_num=0;
page_table[i].frame_num=0;
}
}
}
-
根据输入的进程号进行释放,若查询到页表数据属于此进程的就释放,并把页表和位图都更新了。
6. 测试
伙伴系统
(1)产生测试数据
-
随机为10个进程分别分配和释放内存10次以上
for(int k=0;k<10;k++){
for(int i=0;i<10;i++){
int pid=i+1;
int r=rand()%6+3;
int size=1;
for(int j=0;j<r;j++){
size*=2;
}
printf("现在的进程大小:%d 进程号是:%d\n",size,pid);
allocateMemory(pid,size);
}
for(int i=0;i<10;i++){
int pid=i+1;
freeMemoryBlock(pid);
}
mergeMemoryBlocks();
printMemoryBlock();
ID=1;
printf("-----------------------------------------------------------------------------------------------------\n");
}
-
for循环十次,每次产生一个3到8的随机数,2的随机数次幂表示进程的大小,为这个进程分配内存块
-
十个进程都分配后开始释放内存块,边释放边合并
-
每次释放和分配内存都进行一次内存块链表打印
(2)解释结果
-
由于测试结果太长,任取了其中一组数据详细分析,再取四组画图分析。
详细分析:运行结果:
现在的进程大小:16 进程号是:1
当前内存占用情况
内存块号:1 起始地址:0 内存块大小:512 空闲状态:空闲 占用进程号:-1
内存块号:2 起始地址:512 内存块大小:256 空闲状态:空闲 占用进程号:-1
内存块号:3 起始地址:768 内存块大小:128 空闲状态:空闲 占用进程号:-1
内存块号:4 起始地址:896 内存块大小:64 空闲状态:空闲 占用进程号:-1
内存块号:5 起始地址:960 内存块大小:32 空闲状态:空闲 占用进程号:-1
内存块号:6 起始地址:992 内存块大小:16 空闲状态:空闲 占用进程号:-1
内存块号:7 起始地址:1008 内存块大小:16 空闲状态:占有 占用进程号:1
分配成功
现在的进程大小:8 进程号是:2
当前内存占用情况
内存块号:1 起始地址:0 内存块大小:512 空闲状态:空闲 占用进程号:-1
内存块号:2 起始地址:512 内存块大小:256 空闲状态:空闲 占用进程号:-1
内存块号:3 起始地址:768 内存块大小:128 空闲状态:空闲 占用进程号:-1
内存块号:4 起始地址:896 内存块大小:64 空闲状态:空闲 占用进程号:-1
内存块号:5 起始地址:960 内存块大小:32 空闲状态:空闲 占用进程号:-1
内存块号:6 起始地址:992 内存块大小:8 空闲状态:空闲 占用进程号:-1
内存块号:8 起始地址:1000 内存块大小:8 空闲状态:占有 占用进程号:2
内存块号:7 起始地址:1008 内存块大小:16 空闲状态:占有 占用进程号:1
分配成功
现在的进程大小:8 进程号是:3
当前内存占用情况
内存块号:1 起始地址:0 内存块大小:512 空闲状态:空闲 占用进程号:-1
内存块号:2 起始地址:512 内存块大小:256 空闲状态:空闲 占用进程号:-1
内存块号:3 起始地址:768 内存块大小:128 空闲状态:空闲 占用进程号:-1
内存块号:4 起始地址:896 内存块大小:64 空闲状态:空闲 占用进程号:-1
内存块号:5 起始地址:960 内存块大小:32 空闲状态:空闲 占用进程号:-1
内存块号:6 起始地址:992 内存块大小:8 空闲状态:占有 占用进程号:3
内存块号:8 起始地址:1000 内存块大小:8 空闲状态:占有 占用进程号:2
内存块号:7 起始地址:1008 内存块大小:16 空闲状态:占有 占用进程号:1
分配成功
现在的进程大小:32 进程号是:4
当前内存占用情况
内存块号:1 起始地址:0 内存块大小:512 空闲状态:空闲 占用进程号:-1
内存块号:2 起始地址:512 内存块大小:256 空闲状态:空闲 占用进程号:-1
内存块号:3 起始地址:768 内存块大小:128 空闲状态:空闲 占用进程号:-1
内存块号:4 起始地址:896 内存块大小:64 空闲状态:空闲 占用进程号:-1
内存块号:5 起始地址:960 内存块大小:32 空闲状态:占有 占用进程号:4
内存块号:6 起始地址:992 内存块大小:8 空闲状态:占有 占用进程号:3
内存块号:8 起始地址:1000 内存块大小:8 空闲状态:占有 占用进程号:2
内存块号:7 起始地址:1008 内存块大小:16 空闲状态:占有 占用进程号:1
分配成功
现在的进程大小:32 进程号是:5
当前内存占用情况
内存块号:1 起始地址:0 内存块大小:512 空闲状态:空闲 占用进程号:-1
内存块号:2 起始地址:512 内存块大小:256 空闲状态:空闲 占用进程号:-1
内存块号:3 起始地址:768 内存块大小:128 空闲状态:空闲 占用进程号:-1
内存块号:4 起始地址:896 内存块大小:32 空闲状态:空闲 占用进程号:-1
内存块号:9 起始地址:928 内存块大小:32 空闲状态:占有 占用进程号:5
内存块号:5 起始地址:960 内存块大小:32 空闲状态:占有 占用进程号:4
内存块号:6 起始地址:992 内存块大小:8 空闲状态:占有 占用进程号:3
内存块号:8 起始地址:1000 内存块大小:8 空闲状态:占有 占用进程号:2
内存块号:7 起始地址:1008 内存块大小:16 空闲状态:占有 占用进程号:1
分配成功
现在的进程大小:8 进程号是:6
当前内存占用情况
内存块号:1 起始地址:0 内存块大小:512 空闲状态:空闲 占用进程号:-1
内存块号:2 起始地址:512 内存块大小:256 空闲状态:空闲 占用进程号:-1
内存块号:3 起始地址:768 内存块大小:128 空闲状态:空闲 占用进程号:-1
内存块号:4 起始地址:896 内存块大小:16 空闲状态:空闲 占用进程号:-1
内存块号:10 起始地址:912 内存块大小:8 空闲状态:空闲 占用进程号:-1
内存块号:11 起始地址:920 内存块大小:8 空闲状态:占有 占用进程号:6
内存块号:9 起始地址:928 内存块大小:32 空闲状态:占有 占用进程号:5
内存块号:5 起始地址:960 内存块大小:32 空闲状态:占有 占用进程号:4
内存块号:6 起始地址:992 内存块大小:8 空闲状态:占有 占用进程号:3
内存块号:8 起始地址:1000 内存块大小:8 空闲状态:占有 占用进程号:2
内存块号:7 起始地址:1008 内存块大小:16 空闲状态:占有 占用进程号:1
分配成功
现在的进程大小:64 进程号是:7
当前内存占用情况
内存块号:1 起始地址:0 内存块大小:512 空闲状态:空闲 占用进程号:-1
内存块号:2 起始地址:512 内存块大小:256 空闲状态:空闲 占用进程号:-1
内存块号:3 起始地址:768 内存块大小:64 空闲状态:空闲 占用进程号:-1
内存块号:12 起始地址:832 内存块大小:64 空闲状态:占有 占用进程号:7
内存块号:4 起始地址:896 内存块大小:16 空闲状态:空闲 占用进程号:-1
内存块号:10 起始地址:912 内存块大小:8 空闲状态:空闲 占用进程号:-1
内存块号:11 起始地址:920 内存块大小:8 空闲状态:占有 占用进程号:6
内存块号:9 起始地址:928 内存块大小:32 空闲状态:占有 占用进程号:5
内存块号:5 起始地址:960 内存块大小:32 空闲状态:占有 占用进程号:4
内存块号:6 起始地址:992 内存块大小:8 空闲状态:占有 占用进程号:3
内存块号:8 起始地址:1000 内存块大小:8 空闲状态:占有 占用进程号:2
内存块号:7 起始地址:1008 内存块大小:16 空闲状态:占有 占用进程号:1
分配成功
现在的进程大小:256 进程号是:8
当前内存占用情况
内存块号:1 起始地址:0 内存块大小:512 空闲状态:空闲 占用进程号:-1
内存块号:2 起始地址:512 内存块大小:256 空闲状态:占有 占用进程号:8
内存块号:3 起始地址:768 内存块大小:64 空闲状态:空闲 占用进程号:-1
内存块号:12 起始地址:832 内存块大小:64 空闲状态:占有 占用进程号:7
内存块号:4 起始地址:896 内存块大小:16 空闲状态:空闲 占用进程号:-1
内存块号:10 起始地址:912 内存块大小:8 空闲状态:空闲 占用进程号:-1
内存块号:11 起始地址:920 内存块大小:8 空闲状态:占有 占用进程号:6
内存块号:9 起始地址:928 内存块大小:32 空闲状态:占有 占用进程号:5
内存块号:5 起始地址:960 内存块大小:32 空闲状态:占有 占用进程号:4
内存块号:6 起始地址:992 内存块大小:8 空闲状态:占有 占用进程号:3
内存块号:8 起始地址:1000 内存块大小:8 空闲状态:占有 占用进程号:2
内存块号:7 起始地址:1008 内存块大小:16 空闲状态:占有 占用进程号:1
分配成功
现在的进程大小:32 进程号是:9
当前内存占用情况
内存块号:1 起始地址:0 内存块大小:512 空闲状态:空闲 占用进程号:-1
内存块号:2 起始地址:512 内存块大小:256 空闲状态:占有 占用进程号:8
内存块号:3 起始地址:768 内存块大小:32 空闲状态:空闲 占用进程号:-1
内存块号:13 起始地址:800 内存块大小:32 空闲状态:占有 占用进程号:9
内存块号:12 起始地址:832 内存块大小:64 空闲状态:占有 占用进程号:7
内存块号:4 起始地址:896 内存块大小:16 空闲状态:空闲 占用进程号:-1
内存块号:10 起始地址:912 内存块大小:8 空闲状态:空闲 占用进程号:-1
内存块号:11 起始地址:920 内存块大小:8 空闲状态:占有 占用进程号:6
内存块号:9 起始地址:928 内存块大小:32 空闲状态:占有 占用进程号:5
内存块号:5 起始地址:960 内存块大小:32 空闲状态:占有 占用进程号:4
内存块号:6 起始地址:992 内存块大小:8 空闲状态:占有 占用进程号:3
内存块号:8 起始地址:1000 内存块大小:8 空闲状态:占有 占用进程号:2
内存块号:7 起始地址:1008 内存块大小:16 空闲状态:占有 占用进程号:1
分配成功
现在的进程大小:16 进程号是:10
当前内存占用情况
内存块号:1 起始地址:0 内存块大小:512 空闲状态:空闲 占用进程号:-1
内存块号:2 起始地址:512 内存块大小:256 空闲状态:占有 占用进程号:8
内存块号:3 起始地址:768 内存块大小:32 空闲状态:空闲 占用进程号:-1
内存块号:13 起始地址:800 内存块大小:32 空闲状态:占有 占用进程号:9
内存块号:12 起始地址:832 内存块大小:64 空闲状态:占有 占用进程号:7
内存块号:4 起始地址:896 内存块大小:16 空闲状态:占有 占用进程号:10
内存块号:10 起始地址:912 内存块大小:8 空闲状态:空闲 占用进程号:-1
内存块号:11 起始地址:920 内存块大小:8 空闲状态:占有 占用进程号:6
内存块号:9 起始地址:928 内存块大小:32 空闲状态:占有 占用进程号:5
内存块号:5 起始地址:960 内存块大小:32 空闲状态:占有 占用进程号:4
内存块号:6 起始地址:992 内存块大小:8 空闲状态:占有 占用进程号:3
内存块号:8 起始地址:1000 内存块大小:8 空闲状态:占有 占用进程号:2
内存块号:7 起始地址:1008 内存块大小:16 空闲状态:占有 占用进程号:1
分配成功
合并完成
当前内存占用情况
内存块号:1 起始地址:0 内存块大小:512 空闲状态:空闲 占用进程号:-1
内存块号:2 起始地址:512 内存块大小:256 空闲状态:占有 占用进程号:8
内存块号:3 起始地址:768 内存块大小:32 空闲状态:空闲 占用进程号:-1
内存块号:13 起始地址:800 内存块大小:32 空闲状态:占有 占用进程号:9
内存块号:12 起始地址:832 内存块大小:64 空闲状态:占有 占用进程号:7
内存块号:4 起始地址:896 内存块大小:16 空闲状态:占有 占用进程号:10
内存块号:10 起始地址:912 内存块大小:8 空闲状态:空闲 占用进程号:-1
内存块号:11 起始地址:920 内存块大小:8 空闲状态:占有 占用进程号:6
内存块号:9 起始地址:928 内存块大小:32 空闲状态:占有 占用进程号:5
内存块号:5 起始地址:960 内存块大小:32 空闲状态:占有 占用进程号:4
内存块号:6 起始地址:992 内存块大小:8 空闲状态:占有 占用进程号:3
内存块号:8 起始地址:1000 内存块大小:8 空闲状态:占有 占用进程号:2
内存块号:7 起始地址:1008 内存块大小:16 空闲状态:空闲 占用进程号:-1
释放成功
合并完成
当前内存占用情况
内存块号:1 起始地址:0 内存块大小:512 空闲状态:空闲 占用进程号:-1
内存块号:2 起始地址:512 内存块大小:256 空闲状态:占有 占用进程号:8
内存块号:3 起始地址:768 内存块大小:32 空闲状态:空闲 占用进程号:-1
内存块号:13 起始地址:800 内存块大小:32 空闲状态:占有 占用进程号:9
内存块号:12 起始地址:832 内存块大小:64 空闲状态:占有 占用进程号:7
内存块号:4 起始地址:896 内存块大小:16 空闲状态:占有 占用进程号:10
内存块号:10 起始地址:912 内存块大小:8 空闲状态:空闲 占用进程号:-1
内存块号:11 起始地址:920 内存块大小:8 空闲状态:占有 占用进程号:6
内存块号:9 起始地址:928 内存块大小:32 空闲状态:占有 占用进程号:5
内存块号:5 起始地址:960 内存块大小:32 空闲状态:占有 占用进程号:4
内存块号:6 起始地址:992 内存块大小:8 空闲状态:占有 占用进程号:3
内存块号:8 起始地址:1000 内存块大小:24 空闲状态:空闲 占用进程号:-1
释放成功
合并完成
当前内存占用情况
内存块号:1 起始地址:0 内存块大小:512 空闲状态:空闲 占用进程号:-1
内存块号:2 起始地址:512 内存块大小:256 空闲状态:占有 占用进程号:8
内存块号:3 起始地址:768 内存块大小:32 空闲状态:空闲 占用进程号:-1
内存块号:13 起始地址:800 内存块大小:32 空闲状态:占有 占用进程号:9
内存块号:12 起始地址:832 内存块大小:64 空闲状态:占有 占用进程号:7
内存块号:4 起始地址:896 内存块大小:16 空闲状态:占有 占用进程号:10
内存块号:10 起始地址:912 内存块大小:8 空闲状态:空闲 占用进程号:-1
内存块号:11 起始地址:920 内存块大小:8 空闲状态:占有 占用进程号:6
内存块号:9 起始地址:928 内存块大小:32 空闲状态:占有 占用进程号:5
内存块号:5 起始地址:960 内存块大小:32 空闲状态:占有 占用进程号:4
内存块号:6 起始地址:992 内存块大小:32 空闲状态:空闲 占用进程号:-1
释放成功
合并完成
当前内存占用情况
内存块号:1 起始地址:0 内存块大小:512 空闲状态:空闲 占用进程号:-1
内存块号:2 起始地址:512 内存块大小:256 空闲状态:占有 占用进程号:8
内存块号:3 起始地址:768 内存块大小:32 空闲状态:空闲 占用进程号:-1
内存块号:13 起始地址:800 内存块大小:32 空闲状态:占有 占用进程号:9
内存块号:12 起始地址:832 内存块大小:64 空闲状态:占有 占用进程号:7
内存块号:4 起始地址:896 内存块大小:16 空闲状态:占有 占用进程号:10
内存块号:10 起始地址:912 内存块大小:8 空闲状态:空闲 占用进程号:-1
内存块号:11 起始地址:920 内存块大小:8 空闲状态:占有 占用进程号:6
内存块号:9 起始地址:928 内存块大小:32 空闲状态:占有 占用进程号:5
内存块号:5 起始地址:960 内存块大小:64 空闲状态:空闲 占用进程号:-1
释放成功
合并完成
当前内存占用情况
内存块号:1 起始地址:0 内存块大小:512 空闲状态:空闲 占用进程号:-1
内存块号:2 起始地址:512 内存块大小:256 空闲状态:占有 占用进程号:8
内存块号:3 起始地址:768 内存块大小:32 空闲状态:空闲 占用进程号:-1
内存块号:13 起始地址:800 内存块大小:32 空闲状态:占有 占用进程号:9
内存块号:12 起始地址:832 内存块大小:64 空闲状态:占有 占用进程号:7
内存块号:4 起始地址:896 内存块大小:16 空闲状态:占有 占用进程号:10
内存块号:10 起始地址:912 内存块大小:8 空闲状态:空闲 占用进程号:-1
内存块号:11 起始地址:920 内存块大小:8 空闲状态:占有 占用进程号:6
内存块号:9 起始地址:928 内存块大小:96 空闲状态:空闲 占用进程号:-1
释放成功
合并完成
当前内存占用情况
内存块号:1 起始地址:0 内存块大小:512 空闲状态:空闲 占用进程号:-1
内存块号:2 起始地址:512 内存块大小:256 空闲状态:占有 占用进程号:8
内存块号:3 起始地址:768 内存块大小:32 空闲状态:空闲 占用进程号:-1
内存块号:13 起始地址:800 内存块大小:32 空闲状态:占有 占用进程号:9
内存块号:12 起始地址:832 内存块大小:64 空闲状态:占有 占用进程号:7
内存块号:4 起始地址:896 内存块大小:16 空闲状态:占有 占用进程号:10
内存块号:10 起始地址:912 内存块大小:16 空闲状态:空闲 占用进程号:-1
内存块号:9 起始地址:928 内存块大小:96 空闲状态:空闲 占用进程号:-1
释放成功
合并完成
当前内存占用情况
内存块号:1 起始地址:0 内存块大小:512 空闲状态:空闲 占用进程号:-1
内存块号:2 起始地址:512 内存块大小:256 空闲状态:占有 占用进程号:8
内存块号:3 起始地址:768 内存块大小:32 空闲状态:空闲 占用进程号:-1
内存块号:13 起始地址:800 内存块大小:32 空闲状态:占有 占用进程号:9
内存块号:12 起始地址:832 内存块大小:64 空闲状态:空闲 占用进程号:-1
内存块号:4 起始地址:896 内存块大小:16 空闲状态:占有 占用进程号:10
内存块号:10 起始地址:912 内存块大小:112 空闲状态:空闲 占用进程号:-1
释放成功
合并完成
当前内存占用情况
内存块号:1 起始地址:0 内存块大小:768 空闲状态:空闲 占用进程号:-1
内存块号:3 起始地址:768 内存块大小:32 空闲状态:空闲 占用进程号:-1
内存块号:13 起始地址:800 内存块大小:32 空闲状态:占有 占用进程号:9
内存块号:12 起始地址:832 内存块大小:64 空闲状态:空闲 占用进程号:-1
内存块号:4 起始地址:896 内存块大小:16 空闲状态:占有 占用进程号:10
内存块号:10 起始地址:912 内存块大小:112 空闲状态:空闲 占用进程号:-1
释放成功
合并完成
当前内存占用情况
内存块号:1 起始地址:0 内存块大小:800 空闲状态:空闲 占用进程号:-1
内存块号:13 起始地址:800 内存块大小:96 空闲状态:空闲 占用进程号:-1
内存块号:4 起始地址:896 内存块大小:16 空闲状态:占有 占用进程号:10
内存块号:10 起始地址:912 内存块大小:112 空闲状态:空闲 占用进程号:-1
释放成功
合并完成
当前内存占用情况
内存块号:1 起始地址:0 内存块大小:896 空闲状态:空闲 占用进程号:-1
内存块号:4 起始地址:896 内存块大小:128 空闲状态:空闲 占用进程号:-1
释放成功
合并完成
当前内存占用情况
内存块号:1 起始地址:0 内存块大小:1024 空闲状态:空闲 占用进程号:-1
-
图解进程分配内存空间的过程
-
进程释放内存空间的过程:
-
观察运行结果可以发现每次释放一个进程,释放的顺序按照进程号的顺序(1、2、3......10)
-
每次释放完进程都会合并空闲分区,如果有相邻的两个空闲分区就合并,观察运行结果也是符合预期的。
-
任取四组数据观察内存分配情况:
-
红色为非空闲,绿色为空闲
分页内存分配
(1)产生测试数据
-
创造五个进程,进程号为1~5,进程大小随机,先创造1~4号进程,然后释放3号进程,然后再创建5号进程,观察5号进程会不会使用3号释放后的进程。
srand(time(NULL));
int process_size = (1<<(rand()%5+3));
printf("进程大小为%d\n",process_size);
allocate(1,process_size);
process_size = (1<<(rand()%10+3));
printf("进程大小为%d\n",process_size);
allocate(2,process_size);
process_size = (1<<(rand()%10+3));
printf("进程大小为%d\n",process_size);
allocate(3,process_size);
process_size = (1<<(rand()%10+3));
printf("进程大小为%d\n",process_size);
allocate(4,process_size);
free_memory(3);
process_size = (1<<(rand()%10+3));
printf("进程大小为%d\n",process_size);
allocate(5,process_size);
print_memory();
(2)解释结果
运行结果:
-
可以观察,5号元素会使用3号元素释放的内存空间
进程大小为8
进程大小为64
进程大小为64
进程大小为16
进程大小为128
这是1号进程的第1页,逻辑地址是0,物理地址是0
这是1号进程的第2页,逻辑地址是1,物理地址是4
这是2号进程的第1页,逻辑地址是2,物理地址是8
这是2号进程的第2页,逻辑地址是3,物理地址是12
这是2号进程的第3页,逻辑地址是4,物理地址是16
这是2号进程的第4页,逻辑地址是5,物理地址是20
这是2号进程的第5页,逻辑地址是6,物理地址是24
这是2号进程的第6页,逻辑地址是7,物理地址是28
这是2号进程的第7页,逻辑地址是8,物理地址是32
这是2号进程的第8页,逻辑地址是9,物理地址是36
这是2号进程的第9页,逻辑地址是10,物理地址是40
这是2号进程的第10页,逻辑地址是11,物理地址是44
这是2号进程的第11页,逻辑地址是12,物理地址是48
这是2号进程的第12页,逻辑地址是13,物理地址是52
这是2号进程的第13页,逻辑地址是14,物理地址是56
这是2号进程的第14页,逻辑地址是15,物理地址是60
这是2号进程的第15页,逻辑地址是16,物理地址是64
这是2号进程的第16页,逻辑地址是17,物理地址是68
这是4号进程的第1页,逻辑地址是34,物理地址是136
这是4号进程的第2页,逻辑地址是35,物理地址是140
这是4号进程的第3页,逻辑地址是36,物理地址是144
这是4号进程的第4页,逻辑地址是37,物理地址是148
这是5号进程的第1页,逻辑地址是18,物理地址是72
这是5号进程的第2页,逻辑地址是19,物理地址是76
这是5号进程的第3页,逻辑地址是20,物理地址是80
这是5号进程的第4页,逻辑地址是21,物理地址是84
这是5号进程的第5页,逻辑地址是22,物理地址是88
这是5号进程的第6页,逻辑地址是23,物理地址是92
这是5号进程的第7页,逻辑地址是24,物理地址是96
这是5号进程的第8页,逻辑地址是25,物理地址是100
这是5号进程的第9页,逻辑地址是26,物理地址是104
这是5号进程的第10页,逻辑地址是27,物理地址是108
这是5号进程的第11页,逻辑地址是28,物理地址是112
这是5号进程的第12页,逻辑地址是29,物理地址是116
这是5号进程的第13页,逻辑地址是30,物理地址是120
这是5号进程的第14页,逻辑地址是31,物理地址是124
这是5号进程的第15页,逻辑地址是32,物理地址是128
这是5号进程的第16页,逻辑地址是33,物理地址是132
这是5号进程的第17页,逻辑地址是38,物理地址是152
这是5号进程的第18页,逻辑地址是39,物理地址是156
这是5号进程的第19页,逻辑地址是40,物理地址是160
这是5号进程的第20页,逻辑地址是41,物理地址是164
这是5号进程的第21页,逻辑地址是42,物理地址是168
这是5号进程的第22页,逻辑地址是43,物理地址是172
这是5号进程的第23页,逻辑地址是44,物理地址是176
这是5号进程的第24页,逻辑地址是45,物理地址是180
这是5号进程的第25页,逻辑地址是46,物理地址是184
这是5号进程的第26页,逻辑地址是47,物理地址是188
这是5号进程的第27页,逻辑地址是48,物理地址是192
这是5号进程的第28页,逻辑地址是49,物理地址是196
这是5号进程的第29页,逻辑地址是50,物理地址是200
这是5号进程的第30页,逻辑地址是51,物理地址是204
这是5号进程的第31页,逻辑地址是52,物理地址是208
这是5号进程的第32页,逻辑地址是53,物理地址是212