操作系统-第9天 内存的管理



前言:在《30天自制操作系统》中,第九天介绍了内存的两种管理方式,在此理清一下思路。

1.为什么需要管理内存
          管理内存无非就是管理一些地址,那么为什么要管理地址呢? 如果程序A需要分配100KB的大小的内存,程序B需要分配200KB的大小的内存,那么如果我们随便分配(其实分配说白了就是指定一个内存的地址)的话,A程序的在内存中的数据会与B程序的数据互相覆盖,导致程序出错;而且如果程序结束了,那么内存中的数据不再需要,因此会释放(将使用完的内存归还给内存管理程序)内存。所以这些都是我们要考虑的问题。
 

2.管理内存方式一:   
以4KB为一个单位进行管理,每一个单位配置一个标志位表示这4KB在不在使用(使用中:1 未使用:0)如下图所示:


(假设内存128MB) 这部分代码很简单,只要创建一个数组将里面的数据都初始化为0就可以,那么现在程序A需要分配一段100KB的内存空间,那么我们只需要遍历数组找到连续的25个0即可,程序如下:

<span style="font-size:18px;">        int a[32768],i=0,j=0;
	for(;i<25;i++)
	{
	    if(a[i+j]!=0)
	   {
               j=j+i;
		if(j<32768-25)
		{
                      return 0;
		}
	   }
	}</span>

找到数组的位置j(j表示25个连续0的第一个位置)之后,这个j只是在数组中的位置,我们要知道在内存中的地址,由于数组每个元素管理4KB=0x1000B因此实际的地址是addr=j*0x1000;将这25个数组标记为1,表示正在使用。代码:

<span style="font-size:18px;">  for(i=0;i<25;i++)       
  {
	    //addr代表内存中的地址
            a[addr+i]=1;
  } </span>


下面A程序结束,需要释放内存,那么就将这25个数组标记为0,代码:

  <span style="font-size:18px;">          j=addr/0x1000
            for(i=0;i<25;i++)
            {
	      a[addr+i]=0;
            }</span>
上面就是第一种方案一(windows的软盘管理方法),让我们计算一下算法的复杂度:(假设内存大小为128MB)

(1)首先从空间来看:每个4KB的小内存块都要分配1B的内存用于记录,那么需要128/4KB=32KB的存储空间来存储,大约占总内存的32KB/128MB=0.02%(并不是太大)。

(2)其次从时间来看:刚开始给标记数组分配需要for循环32*1024次,而且分配内存的时候需要查找连续的单元。

(3)我们也可以将标记数组用BYTE(位)来表示,那么需要的存储空间大小为32/8=4KB(一种优化)


3.内存的管理方式二(列表管理):

               这种方式与第一种有点像,同样是分配数组记录每一块内存的信息,但是数组里保存的并不是(4K内存块是否使用)标志位了,而保存的是每一块空闲内存的起始地址和大小。看下图



在这里我们需要建立管理内存的‘机制’,即结构体,想想需要哪些结构体,结构体又保存了什么?

首先每个空闲内存块需要有个结构体表示,至少含有开始地址和(空闲)内存块大小,然后需要一个大的结构体来保存这么多的空闲块的信息,即保存空闲块信息的数组以及空闲块的个数。建立结构体如下:

<span style="font-size:18px;">struct FREEINFO
{
	unsigned int addr,size;//空闲块开始地址和空闲块的大小 
};
struct MEMMAN
{
	unsigned int frees;//空闲块的个数
	struct FREEINFO freeInfo[MEMMAX_FREE]; 
} </span>
接下来讨论分配内存:

首先我们需要找到内存中的空闲块,这个空闲块比我们需要分配的大小要大,依次遍历空闲块数组直至找到满足大小的为止。

<span style="font-size:18px;">unsigned int mem_alloc(struct MEMMAN *m,unsigned int size)//size是需要分配的内存块的大小 
{
	unsigned int i,a; 
	for(i=0;i<frees;i++)
	{
		//如果找到了合适的空闲内存块 
		if(m->freeInfo[i].size>=size)
		{
			//保存地址 
			a=m->freeInfo[i].addr;
			m->freeInfo[i].size-=size;
			m->freeInfo[i].addr+=size;
			//如果与空闲内存大小刚好相等 ,
			//那么空闲内存总数减一,并且从i开始的每个数组元素往前移一位 
			if(m->freeInfo[i].size==0)
			{
				m->frees--;
				for(;i<m->frees;i++)
				{
					m->freeInfo[i]=m->freeInfo[i+1];
				}
			}
		}
	}
	//返回分配内存的起始地址 
	return a;
}</span>
接下来比较麻烦的就是释放内存,我们可以预见以下情况。

(1)当释放的内存上边界和上一个空闲内存块相接壤(地址邻接),那么内存就需要与上一块归并;

(2)当释放的内存下边界和下一个空闲内存块相接壤(地址邻接),那么内存就需要与下一块归并;

(3)以上两种情况都符合

(4)以上两种情况都不发生

<span style="font-size:18px;">//释放内存
void mem_free(struct MEMMAN *m,unsigned int addr,unsigned int size)
{
	int i;
	for(i=0;i<m->frees;i++)
	{
		if(m->freeInfo[i].addr>addr)
		{
			break;
		}
	}
	//1.释放之后与上一个内存块相接壤
	if(i>0)
	{
		//与前面接壤,不接壤就退出 
		if(m->freeInfo[i-1].addr+m->freeInfo[i-1].size==addr)
		{
			m->freeInfo[i-1].size+=addr;
			if(i<m->frees)
			{
				if(addr+size==m->freeInfo[i].addr)
				{
					m->freeInfo[i].addr=addr;
					m->freeInfo[i-1].size+=m->freeInfo[i].size;
					m->frees--;
					for(;i<m->frees;i++)
					{
						m->freeInfo[i]=m->freeInfo[i+1];
					}
				}
			}
			return;
		}
	}
	
	
	//2.释放之后与下一个内存块相接壤
	if(i<m->frees)
	{
		if(addr+size==m->freeInfo[i].addr)
		{
			m->freeInfo[i].addr=addr;
			m->freeInfo[i].size+=size;
			return; 
		}
	}
	//3.释放之后与两边都不接壤 
	if(m->frees<MEMMAX_FREES)
	{
		for(;i<m->frees;i++)
		{
			m->freeInfo[i]=m->freeInfo[i+1];
		}
		m->frees++;
		if(m->frees>m->freeMax)
		{
			m->freeMax++;
		}
		m->freeInfo[i].addr=addr;
		m->freeInfo[i].size=size;
	}
} </span>

上面是《30天自制操作系统》的内存的两种管理方式,正在学习其他类型的内存管理方式,因此后面我会补充更多的内容。



 


    





  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值