嵌入式实时操作系统的设计与开发 (伙伴系统内存分配)

#define acoral_malloc(size) buddy_malloc(size)

传入的参数是用户需要的内存大小,这个函数将需要内存的大小转换成可以分配满足这个大小的内存块所对应的逻辑层。

例如,基本内存块的大小为1KB,而申请的内存为5KB,则分配8KB,则通过函数返回3,即所对应的逻辑层为3。实际分配的内存为基本内存块的2m(m为层数,该例子中为3)。

若申请的内存不是2m基本块大小的,则取大于申请的内存的最小2m大小。

如果系统中剩余的基本内存块小于申请的内存对应的内存块的数量,则不分配。如果申请的内存大于最高层的内存块的大小,则越界了,也不分配。

void *buddy_malloc(unsigned int size)
{
	unsigned int resize_size;
	unsigned char level = 0;
	unsigned int num = 1;
	resize_size = BASIC_BLOCK_SIZE;
	if(acoral_mem_ctrl->state == MEM_NO_ALLOC)
		return NULL;
	while(resize_size < size) //本层块大小不满足申请内存
	{
		num = num << 1;
		level++;
		resize_size = resize_size << 1;
	}
	if(num > acoral_mem_ctrl->free_num) //剩余内存不足
		return NULL;
	if(level >= acoral_mem_ctrl->level) //申请内存块大小超过顶层内存块大小
		return NULL;
	return r_malloc(level); //实际的分配函数
}
/*
	伙伴系统实际内存分配
	level:要分配的层数,起始为0
	return:void* 返回分配的地址
*/
static void *rt_malloc(unsigned char level)
{
	unsigned int index;
	int num,cur;
	acoral_enter_critical();
	acoral_mem_ctrl->free_num -= 1 << level; //提前减去即将分配的基本内存块
	/*
		获取该层空闲内存块链表头free_cur指向的空闲节点。
		如果该层没有空闲内存块(即cur<0),即链表头指向-1,在伙伴系统初始化时,free_cur是等于0的,即指向该层的第一个空闲内存块,随着内存块的分配,free_cur也会变化,当该层的内存块分配完后,被置为-1。
	*/
	cur = acoral_mem_ctrl->free_cur[level];
	/*	
		向上层申请,recus_malloc()返回空闲内存块的第一个基本内存块的序号num
	*/
	if(cur < 0)
	{
		num = recus_malloc(level+1);
		if(num < 0)
		{
			acoral_exit_critical();
			return NULL;
		}
		/*
			acoral_set_bit()根据num值计算得到逻辑层空闲内存块所对应的位图位的编号(根据num可确定其对应的某一层逻辑内存块的序号,根据该序号,可以确定其所对应的bitmap位),即该层的空闲内存块对应了该层bitmap的哪一位。上层分配下来的内存块,包含两个此层的内存块,一块要使用,另一块必然是空闲的,因此要将对应的位图置1,表示相邻两块内存块有一块是空闲的
		*/
		index = num >> level + 1;
		cur = index/32;
		acoral_set_bit(index,acoral_mem_ctrl->bitmap[level]);
		acoral_mem_ctrl->free_list[level][cur] = -1;
		acoral_mem_ctrl->free_cur[level] = cur;
		/*
			将基本内存块序号对应的控制块的level置为该层数值,用来标识以该基本内存块开始的内存块是从该层分配的
		*/
		if((num & 0x1) == 0)
			acoral_mem_blocks[BLOCK_INDEX(num)].level = level;
		acoral_exit_critical();
		/*
			返回分配的内存的地址,根据基本内存块的序号即可知道该基本内存块的地址,也就是要分配的地址,优先使用第一块,故基本内存块的编号就是上层分配下来的基本内存块的序号。
		*/
		return (void *)(acoral_mem_ctrl->start_adr + (num << BLOCK_SHIFT));
	}


	/*
		相对于代码的cur<0,如果当前有空闲的内存块cur>=0,则读取空闲链表中的第一个含有空闲内存块对应的位图块,选择该内存位图块第一位为1对应的内存块,并得到该内存块的序号。acoral_ff3返回32位的第一个非零的位置
	*/
	index = acoral_ffs(acoral_mem_ctrl->bitmap[level][cur]);
	index = index + cur * 32;
	//清除该内存块对应的内存块位图的1位,同时如果该内存块所在的位图块为0,修改空闲链表,将链表头指向下一个含有空闲内存块的位图块。
	acoral_clear_bit(index,acoral_mem_ctrl->bitmap[level]);
	if(acoral_mem_ctrl->bitmap[level][cur] == 0)
	{
		acoral_mem_ctrl->free_cur[level] = acoral_mem_ctrl->free_list[level][cur];
	}


	/*我们知道0至(level-2)层的内存位图块的某一位代表了相邻的两个兄弟内存块,而第一个内存块和第二个内存块对应的基本内存块的序号是不一样的,第一块对应的序号是index<<1+level,第二块对应的序号是(index<<1+level)+ 1+level*/
	//最高层level-1,因为此层的内存位图块一位只对应一个内存块,其内存块对应的基本内存块的序号是index<
	if (level == acoral_mem_ctrl->level - 1)
	{
		num = index << level;
		if (num + (1 << level) > acoral_mem_ctrl->block_num)
		{
			acoral_exit_critical();
			return NULL;
		}
	}
	else
	{
		num = index << level + 1;
		if (acoral_mem_blocks[BLOCK_INDEX(num)].level >= 0)
			num += (1 << level);
	}
	if ((num & 0x1) == 0)
		acoral_mem_blocks[BLOCK_INDEX(num)].level = level;
#ifdef CFG_TEST_MEM
	buddy_scan();
#endif
	acoral_exit_critical();
	return (void *)(acoral_mem_ctrl->start_adr + (num << BLOCK_SHIFT));
}

当某一内存块被分配完后,向更上一层申请内存块

/*
	迭代获取空闲块的首num
	level:要获取的层数,起始为0
	return:int 空闲块的首num
*/

static int recus_malloc(int level)
{
	unsigned int index;
	int cur;
	int num;
	if (level >= acoral_mem_ctrl->level) // 层数超出范围
		return -1;
	cur = acoral_mem_ctrl->free_cur[level]; // 获取首个空闲位图
	if (cur < 0)							// 无空闲
	{
		num = recus_malloc(level + 1); // 迭代向上寻找
		if (num < 0)
			return -1;
		index = num >> level + 1;							   // 计算在位图中位置
		cur = index / 32;									   // 计算在位图链表中位置
		acoral_set_bit(index, acoral_mem_ctrl->bitmap[level]); // 在位图中置1,把偶数块分出去
		// 当前层无空闲,两个空闲块是从上层分配的,所以空闲位图链表更改,然后分配完一块还有一块,所以移动空闲位图链表头
		acoral_mem_ctrl->free_list[level][cur] = -1;
		acoral_mem_ctrl->free_cur[level] = cur;
		return num;
	}
	index = acoral_ffs(acoral_mem_ctrl->bitmap[level][cur]); // 获取空闲块在其32位图中的位置
	index = cur * 32 + index;								 // 计算空闲块实际位置
	acoral_clear_bit(index, acoral_mem_ctrl->bitmap[level]); // 从只有一块空闲变成两块都不空闲了,所以清0
	if (acoral_mem_ctrl->bitmap[level][cur] == 0)			 // 如果此位图无空闲块了
	{
		acoral_mem_ctrl->free_cur[level] = acoral_mem_ctrl->free_list[level][cur]; // 移动空闲位图链表头
	}
	num = index << level + 1; // 计算空闲块首个num
	/*最高level情况*/
	if (level == acoral_mem_ctrl->level - 1) // 最大内存块层
	{
		if ((num >> 1) + (1 << level) > acoral_mem_ctrl->block_num)
			return -1;
		return num >> 1;
	}
	// 其余层
	if (acoral_mem_blocks[BLOCK_INDEX(num)].level >= 0) // 检查这块内存有没有被分配
	{
		return num + (1 << level);
	} // 如果此块已经被分配,那就是后面一块为空闲块
	else
	{
		return num;
	}
}
#define acoral_free(ptr) buddy_free(ptr)

传入的参数是回收内存的起始地址

你可能感兴趣的:(嵌入式实时操作系统的设计与开发,算法,嵌入式实时操作系统的设计与开发,嵌入式实时操作系统)