C语言:伙伴系统


1 伙伴系统

  • 边界标志法相比没有尾信息
  • 分配内存块大小: 2n
  • 由同一个内存块分割成的两个完全相等的内存块互为伙伴。
  • 内存碎片化严重
  • 回收时必须互为伙伴才能合并
    不是伙伴,无法确定另一个内存块的边界

2 C语言实现

2.1 头文件

#pragma once

#define M 16

typedef struct Word//定义节点
{
	struct Word* llink;//前驱
	int tag;//0为空闲,1为占用
	int kval;//k值,内存块的大小2^k
	struct Word* rlink;//后继
}Word, * Space;

typedef struct HeadNode//定义顺序表节点
{
	int nodesize;//内存空间的大小
	Word* first;//表映射内存空间的指针
	Word* start;//记录内存池的首的地址
}FreeList[M+1];//顺序表

Space CreateMem();//创建内存池

void InitSqlist(FreeList& avail,Space p);//初始化顺序表

Word* AllocBuddy(FreeList& avail, int n);//分配内存单元

void Free(FreeList& avail, Space p);//回收内存

2.2 创建内存池

#include"buddy.h"
#include<stdio.h>
#include <malloc.h>
#include<math.h>
#include <cassert>

//Word* start;//定义全局变量记录内存池的开始地址,也可以在表的结构体中记录内存池的首的地址

Space CreateMem()
{
	Word* pmem = (Word*)malloc((int)pow(2, M) * sizeof(Word));// 创建内存池
	assert(pmem != NULL);

	pmem->llink = pmem;
	pmem->rlink = pmem;
	pmem->tag = 0;
	pmem->kval = M;
	//start = pmem;

	return pmem;
}

2.3 初始化内存映射顺序表

void InitSqlist(FreeList& avail,Space p)//初始化内存映射表
{
	avail[0].start = p;

	for (int i = 0; i <= M; i++)
	{
		avail[i].first = NULL;
		avail[i].nodesize = 0;
	}
	int k = p->kval;
	avail[k].first = p;
	avail[k].nodesize = (int)pow(2, k);

}

2.4 分配内存单元

2.4.1 从内存映射表中删除该内存单元

static void Delete(FreeList& avail, Space p)//从对应表中删除该内存单元
{
	Word* front = p->llink;
	Word* rear = p->rlink;
	if (p == rear)//合并后,对应表内没有其他内存单元,将对应表置空
	{
		avail[p->kval].first = NULL;
	}
	else//合并后,对应表内有其他内存单元,从表中删除该空间单元p
	{
		front->rlink = rear;
		rear->llink = front;
		avail[p->kval].first = rear;

		p->llink = p;
		p->rlink = p;
	}
}

2.4.2 分配内存单元,将剩余空间分裂

Word* AllocBuddy(FreeList& avail, int n)//分配内存单元
{
	Word* p = NULL;//分配的内存单元

	for (int k = 0; k <= M; ++k)
	{
		if (k > M)
		{
			return NULL;
		}
		else if (avail[k].nodesize >= n && avail[k].first != NULL)
		{
			p = avail[k].first;
			Delete(avail, p);//从对应表中删除该内存单元

			int i = 1;

			if (p->kval > 0 && avail[k].first == NULL)//将剩余空间分裂,插入表中
			{
				while ((int)pow(2, k - i) >= n)
				{
					Word* pi = p + (int)pow(2, k - i);
					//pi = p + (int)pow(2, k - i);
					pi->rlink = pi;
					pi->llink = pi;
					pi->tag = 0;
					pi->kval = k - i;

					avail[k - i].first = pi;
					avail[k - i].nodesize = (int)pow(2, pi->kval);

					i++;
				}
			}
			p->llink = p;
			p->rlink = p;
			p->tag = 1;
			p->kval = k-(--i);

			break;
		}
	}
	return p;
}

2.5 回收内存

2.5.1 判断要回收的内存单元是左块还是右块

static Word* RorL(FreeList& avail,Space p)//判断左右内存块
{
	//int tmp = p - start;//计算p的相对地址
	int tmp = p - avail[0].start;

	int flg = tmp % (int)pow(2, p->kval + 1);

	if (flg != 0)//p为右块,地址高
	{
		return p - (int)pow(2, p->kval);
	}
	else//p为左块,地址低
	{
		return  p + (int)pow(2, p->kval);
	}

}

2.5.2 将内存单元和顺序表映射

static void Insert(FreeList& avail, Space p)//将内存单元和对应表映射
{
	if (avail[p->kval].first == NULL)//对应表为空,直接映射
	{
		avail[p->kval].first = p;
		p->tag = 0;
	}
	else//对应表不为空,插在对应表指向内存单元的前面
	{
		Word* q1 = avail[p->kval].first;

		Word* front = q1->llink;

		q1->llink = p;
		front->rlink = p;

		p->llink = front;
		p->rlink = q1;
		p->tag = 0;
	}
}

2.5.3 判断顺序表是否为空

bool IsEmpty(FreeList& avail)
{
	for (int i = 0; i <= M; i++)
	{
		if (avail[i].first != NULL)
		{
			return false;
		}
	}
	return true;
}

2.5.4 判断伙伴,进行内存单元的回收合并

static void Free_mask(FreeList& avail, Space& p)//一次回收合并
{                                     //形参p为实参的引用
	Space pi = RorL(avail, p);

	if (pi->tag == 1)//伙伴不空闲
	{
		Insert(avail, p);//看对应表情况,映射
	}
	else if (pi->tag == 0 && pi->kval != p->kval)//伙伴不完整
	{
		Insert(avail, p);//看对应表情况,映射
	}
	else//伙伴空闲
	{
		Delete(avail, pi);//从对应表中删除该内存单元

		p->tag = 0;
		if (p > pi)//让p每次都作为左块
		{
			p = pi;
		}
		p->kval++;//将p的k值++,用于判断合并后的伙伴
	}
}

void Free(FreeList& avail, Space p)//回收内存单元
{
	if (!IsEmpty(avail))
	{
		p->tag = 0;
		Space q = p;
		int k = p->kval;
		while (p->tag == 0 && p->kval == k)//当内存块空闲,且是完整的,进行合并
		{
			Free_mask(avail, p);
			k++;
		}
		if (p->kval == M - 1)//当合并后的大小为内存池的一一半时
		{
			q->kval--;

			avail[q->kval].first = NULL;
			q = q - (int)pow(2, q->kval);
			avail[M].first = q;
			q->llink = q;
			q->rlink = q;
			q->kval = M;
			q->tag = 0;
		}
	}
	else//小于一半大小,直接映射顺序表
	{
		avail[p->kval].first = p;
		p->tag = 0;
	}

}
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
假设系统的可利用空间容量为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种: ⑴ 根据伙伴系统算法分配内存 ⑵ 根据伙伴系统算法回收内存 ⑶ 实时查看内存使用的情况
C语言模拟伙伴系统的设计可以包括以下几个部分。 首先,需要设计一个伙伴内存管理系统。伙伴系统是一种用于内存分配的算法,它根据内存大小的2的幂次来管理内存块。通过使用位图,我们可以追踪内存块的分配情况。当需要分配内存时,系统会在位图中查找合适的块,并将其标记为已分配。当内存释放时,系统会将块重新合并并更新位图。 其次,我们需要设计一个伙伴分配器。伙伴分配器可以接收用户请求的内存大小,并与伙伴系统协调以分配合适大小的内存块。具体来说,分配器将检查伙伴系统位图并搜索合适的块,如果找到的块大小刚好满足用户需求,那么它会被直接分配。如果找到的块大于用户需求,那么它会被一分为二,其中一个块会被分配给用户,而另一个块则会继续划分和分配。如果找不到合适大小的块,则分配器将返回失败。 最后,我们可以实现一些用户接口来测试和操作伙伴系统。例如,我们可以创建一个函数来分配一定大小的内存块,并返回其指针。另外,我们也可以实现一个函数来释放先前分配的内存块。在这些用户接口中,我们需要与伙伴分配器进行交互,并更新位图以反映内存的分配与释放。 通过以上设计,我们可以实现一个基本的C语言模拟伙伴系统。当然,根据实际需求和可扩展性要求,我们还可以对系统进行进一步的优化和改进。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值