从uCOSii中抠出来的内存管理程序
1、学习uCOSii的内存管理的原因
操作系统和内存管理是分不开的,每个操作系统都有自己的一套内存管理方法。在实际应用中,我们尽量使用其自带的内存管理。学习和使用uCOSii也有一段时间了,觉得它的内存管理方法比较传统,所以就系统学习了一下。为了便于理解,特地把内存管理的主要函数从uCOSii中抠出来,即使没有用过uCOSii的人也可以理解了。
重点掌握内存控制块初始化、内存池初始化、节点的申请和释放、内存的信息及使用情况。
2、程序代码分析
#define OS_MAX_MEM_PART 2 //内存池数量为2
#define InternalRAM1_StartAddress 0x20001000
#define InternalRAM1_TotalNumberOfBytes 20 //定义内部堆区的大小为20字节
#define InternalRAM1_BlockSize 4 //每个存储块大小为4,必须为4的倍数
#define InternalRAM1_NumberOfBlock InternalRAM1_TotalNumberOfBytes/InternalRAM1_BlockSize
//存储块的数量
//由于一个指针变量占用4字节,所以块的大小一定要为4的倍数
__align(4) uint8_t InternalRAM1_Array[InternalRAM1_NumberOfBlock][InternalRAM1_BlockSize] __attribute__((at(InternalRAM1_StartAddress)));
#if OS_MAX_MEM_PART >= 2u //至少有2个内存池
#define InternalRAM2_StartAddress (InternalRAM1_StartAddress+InternalRAM1_TotalNumberOfBytes)
#define InternalRAM2_TotalNumberOfBytes 24 //定义内部堆区的大小为24字节
#define InternalRAM2_BlockSize 8 //每个存储块大小为8,必须为4的倍数
#define InternalRAM2_NumberOfBlock InternalRAM2_TotalNumberOfBytes/InternalRAM2_BlockSize
//存储块的数量
//由于一个指针变量占用4字节,所以块的大小一定要为4的倍数
__align(4) uint8_t InternalRAM2_Array[InternalRAM2_NumberOfBlock][InternalRAM2_BlockSize] __attribute__((at(InternalRAM2_StartAddress)));
#endif
typedef struct os_mem
{
void *OSMemAddr; /*根节点地址*/
void *OSMemFreeList;/*下一个节点的地址*/
uint32_t OSMemBlkSize;/*节点的字节数*/
uint32_t OSMemNBlks; /*节点的总数量”*/
uint32_t OSMemNFree;/*可用节点数量*/
}OS_MEM;
OS_MEM *MemoryControlBlock1_Pointer;//内存控制块指针
OS_MEM *MemoryControlBlock2_Pointer;//内存控制块指针
typedef struct os_mem_data
{
void *OSAddr; //根节点地址
void *OSFreeList; //可用的下一个节点的地址
int32_t OSBlkSize; //节点的字节数
int32_t OSNBlks; //节点的总数量
int32_t OSNFree; //可用节点数量
int32_t OSNUsed; //已用节点数量
}OS_MEM_DATA;
OS_MEM_DATA My_OS_MEM_DATA;
#define OS_SUCCESS 0u
#define OS_FAIL 1u
#define OS_FULL 2u
函数功能:对两个内存控制块进行初始化,OSMemFreeList指向"内存控制块0的首地址"
void OS_MemInit(void)
{
int8_t *pdest;//声明pdest为"一维指针"
int16_t size;
OS_MEM *pmem;//声明pmem为"OS_MEM型结构指针"
int16_t current,next;
pdest=(int8_t *)&OSMemTbl[0];//将"内存控制块0的首地址"保存到pdest
size=sizeof(OSMemTbl);//求内存控制块OSMemTbl[]有多少个字节
while(size>0)//将内存控制块清0
{
*pdest++ = (int8_t)0;
size--;
}
#if OS_MAX_MEM_PART == 1u //只有1个内存池
OSMemFreeList=(OS_MEM *)&OSMemTbl[0];
//将"内存控制块0的首地址"保存到OSMemFreeList
#endif
#if OS_MAX_MEM_PART >= 2u //至少有2个内存池
for(current=0;current< OS_MAX_MEM_PART-1;current++)
{
pmem = &OSMemTbl[current];
//将"内存控制块current的首地址"保存到pmem
next=current+1;
pmem->OSMemFreeList = (void *)&OSMemTbl[next];
//将"内存控制块next的首地址"保存到pmem->OSMemFreeList
}
pmem= &OSMemTbl[current];
//将"内存控制块current的首地址"保存到pmem
//这里的OSMemTbl[current]是最后一个内存控制块
pmem->OSMemFreeList=(void *)0;
/*由于"最后一个内存控制块"没有"下一个内存控制块"了,所有设置"下一个内存控制块的地址"为0*/
OSMemFreeList=(OS_MEM *)&OSMemTbl[0];
//将"内存控制块0的首地址"保存到OSMemFreeList
#endif
}
//函数功能:初始化内存池和内存控制块
//addr为根节点地址
//nblks为节点的总数量
//blksize为节点的字节数
//返回值为内存控制块指针
OS_MEM *OSMemCreate (void *addr,uint32_t nblks,uint32_t blksize,uint8_t *perr)
{
OS_MEM *pmem;//声明pmem为"OS_MEM型结构指针"
uint8_t *pblk;//声明pblk为"一维指针"
void **plink;//声明plink为"void型二维指针"
uint32_t loops;
uint32_t i;
if(addr == (void *)0)//根节点地址addr为0
{
*perr = OS_FAIL;
return ((OS_MEM *)0);
}
if(((int32_t)addr & (sizeof(void *) - 1u)) != 0u) // addr不是4字节对齐
{
*perr = OS_FAIL;
return ((OS_MEM *)0);
}
if(nblks < 2)//节点的总数量只有1个
{
*perr = OS_FAIL;
return ((OS_MEM *)0);
}
if (blksize < sizeof(void *))//节点的字节数小于4
{
*perr = OS_FAIL;
return ((OS_MEM *)0);
}
pmem = OSMemFreeList;//读取内存控制块指针
if (OSMemFreeList != (OS_MEM *)0)
{
OSMemFreeList = (OS_MEM *)OSMemFreeList->OSMemFreeList;
/*将"下一个内存控制块的首地址"保存到OSMemFreeList,为初始化下一个内存池做准备*/
}
plink = (void **)addr;
//将"void型一维指针addr"强制转换为"void型二维指针"保存到plink中
pblk = (uint8_t *)addr;
//将"void型一维指针addr"强制转换为"uint8_t型一维指针"保存到pblk中
loops = nblks-1;//nblks为节点的总数量
for(i=0;i<loops;i++)
{
pblk +=blksize;
//将"下一个节点的首地"址保存到"uint8_t型一维指针pblk"
*plink = (void *)pblk;
/*将"uint8_t型一维指针pblk"强制转换为"void型一维指针"保存到"void型一维指针(*plink)"中*/
plink = (void **)pblk;
/*将"uint8_t型一维指针pblk"强制转换为"void型二维指针",保存到"void型二维指针plink"中*/
}
*plink = (void *)0;
//由于"最后一个节点"没有"下一个节点"了,所有设置"下一个节点的地址"为0
pmem->OSMemAddr= addr;
pmem->OSMemFreeList = addr;
pmem->OSMemNFree= nblks;
pmem->OSMemNBlks = nblks;
pmem->OSMemBlkSize = blksize;
return (pmem);//返回内存控制块指针
}
//函数功能:根据"内存控制块指针pmem",申请一个节点
//pmem为内存控制块指针
void *OSMemGet(OS_MEM *pmem,uint8_t *perr)
{
void *pblk;//声明pblk为"void型一维指针"
if (pmem == (OS_MEM *)0)
{//内存控制块指针pmem为0
*perr = OS_FAIL;
return ((void *)0);
}
if(pmem->OSMemNFree > 0u)//"空闲节点计数器"大于0,说明有空闲的节点存在
{
pblk = pmem->OSMemFreeList;
/*从内存控制块中,读取下一个节点的首地址,并将它保存到pblk中,这就是"申请到的节点"的首地址*/
pmem->OSMemFreeList = *(void **)pblk;
/*在内存池中,将"pblk为首地址数据块"强制转换为"void型二维指针",其"所指向单元的内容"为指针数据*/
/*将读到的指针数据保存到pmem->OSMemFreeList中,这就是"申请到的节点"的下一个节点的首地址*/
pmem->OSMemNFree--;//空闲节点计数器减1;
*perr = OS_SUCCESS;//建立成功标志
return (pblk);//返回"申请到的节点"的首地址
}
*perr=OS_FAIL;//建立失败标志
return ((void *)0);//返回0
}
//函数功能:释放一个节点
//pmem为内存控制块指针
//pblk为待释放的节点指针
int8_t OSMemPut(OS_MEM *pmem,void *pblk)
{
if(pmem == (OS_MEM *)0) return (OS_FAIL);
if(pblk == (void *)0) return (OS_FAIL);
if(pmem->OSMemNFree >= pmem->OSMemNBlks) return (OS_FULL);
*(void **)pblk = pmem->OSMemFreeList;
//将pblk强制转换为"void型二维指针",其"所指向单元的内容"为指针域
//从内存控制块中,读取"待释放的节点的下一个节点的首地址",并将它保存到指针域
pmem->OSMemFreeList = pblk;
//将"待释放的节点指针"保存到内存控制块中,记录"下一个节点的首地址"
pmem->OSMemNFree++;//空闲节点计数器加1;
return (OS_SUCCESS);
}
int8_t OSMemQuery(OS_MEM *pmem,OS_MEM_DATA *p_mem_data)
{
if (pmem == (OS_MEM *)0) return (OS_FAIL);
if (p_mem_data == (OS_MEM_DATA *)0) return (OS_FAIL);
p_mem_data->OSAddr = pmem->OSMemAddr;
//从内存控制块中读取"根节点地址"
p_mem_data->OSFreeList = pmem->OSMemFreeList;
//从内存控制块中读取"下一个节点的地址"
p_mem_data->OSBlkSize = pmem->OSMemBlkSize;
//从内存控制块中读取"节点的字节数量"
p_mem_data->OSNBlks = pmem->OSMemNBlks;
//从内存控制块中读取"节点的总数量"
p_mem_data->OSNFree = pmem->OSMemNFree;
//从内存控制块中读取"可用节点的数量"
p_mem_data->OSNUsed = p_mem_data->OSNBlks - p_mem_data->OSNFree;
//"已用节点数量" = "节点的总数量" - "可用节点的数量"
return(OS_SUCCESS);
}
const char RootNode_REG[]="\r\nRootNode=0x";
const char NextNode_REG[]=" NextNode=0x";
const char TotalNumberOfNode_REG[]=" TotalNumberOfNode=";
const char SizeOfNode_REG[]=" SizeOfNode=";
const char Bytes_REG[]="Bytes";
const char NumberOfUsedNode_REG[]=" NumberOfUsedNode=";
const char NumberOfFreeNode_REG[]=" NumberOfFreeNode=";
const char Mymemory_REG[]="\r\nMymemory: ";
void Do_OSMemQuery(void)
{
u32 *pblk;//声明pblk为"u32型一维指针"
u8 i;
OSMemQuery(MemoryControlBlock1_Pointer,&My_OS_MEM_DATA);
printf("%s",RootNode_REG);printf("%p",My_OS_MEM_DATA.OSAddr);
//打印根节点地址
printf("%s",NextNode_REG);printf("%p",My_OS_MEM_DATA.OSFreeList);
//打印下一节点地址
//printf("%s",SizeOfNode_REG);printf("%u",My_OS_MEM_DATA.OSBlkSize);printf("%s",Bytes_REG);//打印节点字节数
//printf("%s",NumberOfUsedNode_REG);printf("%u",My_OS_MEM_DATA.OSNUsed);//打印已用节点数量
//printf("%s",NumberOfFreeNode_REG);printf("%u",My_OS_MEM_DATA.OSNFree);//打印未用节点数量
//printf("%s",TotalNumberOfNode_REG);printf("%u",My_OS_MEM_DATA.OSNBlks);//打印节点总数量
printf(" %u + %u = %u",My_OS_MEM_DATA.OSNFree,My_OS_MEM_DATA.OSNUsed,My_OS_MEM_DATA.OSNBlks);
printf("%s",Mymemory_REG);
pblk=(u32 *)&InternalRAM1_Array[0];
for(i=0;i<InternalRAM1_TotalNumberOfBytes/4;i++)
{
printf(" 0x%08x",*pblk);
pblk++;
}
}
void MEMORY_Test(void)
{
u8 err;
u8 *ptr1,*ptr2;
printf("\r\n\r\nReady...");
Do_OSMemQuery();
printf("\r\nmalloc1:");
ptr1 = OSMemGet(MemoryControlBlock1_Pointer,&err);
//根据"内存控制块指针MemoryControlBlock1_Pointer",申请一个节点
if(ptr1!=NULL) *(u32*)ptr1=0x12345678;
Do_OSMemQuery();
printf("\r\nmalloc2:");
ptr2 = OSMemGet(MemoryControlBlock1_Pointer,&err);
if(ptr2!=NULL) *(u32*)ptr2=0x87654321;
Do_OSMemQuery();
printf("\r\nfree1:");
if(ptr2!=NULL)//后申请的先释放
{
OSMemPut(MemoryControlBlock1_Pointer,ptr2);
//释放一个内存块,首地址为ptr2
Do_OSMemQuery();
ptr2=NULL;
}
printf("\r\nfree2:");
if(ptr1!=NULL)//先申请的后释放
{
OSMemPut(MemoryControlBlock1_Pointer,ptr1);
//释放一个内存块,首地址为ptr1
Do_OSMemQuery();
ptr1=NULL;
}
}
测试结果如下:
void Do_OSMemQuery2(void)
{
u32 *pblk;//声明pblk为"u32型一维指针"
u8 i;
OSMemQuery(MemoryControlBlock2_Pointer,&My_OS_MEM_DATA);
printf("%s",RootNode_REG);printf("%p",My_OS_MEM_DATA.OSAddr);
//打印根节点地址
printf("%s",NextNode_REG);printf("%p",My_OS_MEM_DATA.OSFreeList);
//打印下一节点地址
//printf("%s",SizeOfNode_REG);printf("%u",My_OS_MEM_DATA.OSBlkSize);printf("%s",Bytes_REG);//打印节点字节数
//printf("%s",NumberOfUsedNode_REG);printf("%u",My_OS_MEM_DATA.OSNUsed);//打印已用节点数量
//printf("%s",NumberOfFreeNode_REG);printf("%u",My_OS_MEM_DATA.OSNFree);//打印未用节点数量
//printf("%s",TotalNumberOfNode_REG);printf("%u",My_OS_MEM_DATA.OSNBlks);//打印节点总数量
printf(" %u + %u = %u",My_OS_MEM_DATA.OSNFree,My_OS_MEM_DATA.OSNUsed,My_OS_MEM_DATA.OSNBlks);
printf("%s",Mymemory_REG);
pblk=(u32 *)&InternalRAM2_Array[0];
for(i=0;i<InternalRAM2_TotalNumberOfBytes/4;i++)
{
printf(" 0x%08x",*pblk);
pblk++;
}
}
void MEMORY2_Test(void)
{
u8 err;
u8 *ptr1,*ptr2;
printf("\r\n\r\nReady...");
Do_OSMemQuery2();
printf("\r\nmalloc1:");
ptr1 = OSMemGet(MemoryControlBlock2_Pointer,&err);
//根据"内存控制块指针MemoryControlBlock2_Pointer",申请一个节点
if(ptr1!=NULL) *(u32*)ptr1=0x12345678;
Do_OSMemQuery2();
printf("\r\nmalloc2:");
ptr2 = OSMemGet(MemoryControlBlock2_Pointer,&err);
if(ptr2!=NULL) *(u32*)ptr2=0x87654321;
Do_OSMemQuery2();
printf("\r\nfree1:");
if(ptr2!=NULL)//后申请的先释放
{
OSMemPut(MemoryControlBlock2_Pointer,ptr2);
//释放一个内存块,首地址为ptr2
Do_OSMemQuery2();
ptr2=NULL;
}
printf("\r\nfree2:");
if(ptr1!=NULL)//先申请的后释放
{
OSMemPut(MemoryControlBlock2_Pointer,ptr1);
//释放一个内存块,首地址为ptr1
Do_OSMemQuery2();
ptr1=NULL;
}
}
测试结果:
3、程序测试结果