uCOSii内存管理(MEMORY MANAGEMENT)
OSMemGet()执行一次,只能申请一个节点,由于节点的数据长度是固定的,所以可用内存的长度是固定的,这一点和malloc()不同。
OSMemPut()执行一次,只释放一个节点,这一点和free()用法差不多,不用关心内存的长度,只要知道内存首地址就可以释放内存。
内存碎片,就是"可用的节点"被"已用的节点"分成多个、互不相连的小数据块。这时,若我们再想申请很大的内存,就不可能了。而uCOSii规避了这个问题,因为申请一次内存最多也就是一个节点,不允许你申请更多的节点,所以,就没有碎片之说。也就是说,即使"可用的节点"被"已用的节点"分成多个、互不相连的小数据块,你也只能申请一个节点,所以这种担心是多余的。所以uCOSii可以随时释放,不作要求,不分先后,因为它没有内存碎片之说。但释放内存还是按照"先申请后释放"原则,要养成习惯。
注意:在uCOSii中,可用的节点含有指针域,一旦该节点被使用了,指针域变成了数据域的一部分。这和其他系统可能不一样。
由于节点的大小是固定的,可以创建不同的链表,供系统使用,节点最小为4个字节。
当指针为4字节时,链表必须是以4字节对齐的数据块,它是由一系列节点组成的;
每个节点也是一个以4字节对齐的数据块,我们叫它小数据块或叫节点;
这个小数据块分为两个部分:
一部分是存储下一个节点地址的指针域,另外一部分是存储数据的数据域;
链表的节点可能是连续的,但也可能是离散的;
链表的第一个节点,称为根节点,通过它可以找到其他节点中的数据;
uCOSii中使用的是单向链表,最后一个节点的指针域是空的;
数组是通过开辟一段连续的内存来存储数据,它的每个元素和链表中的节点相对应;
由于数组是连续的数据块,因此指针不用保存,每个元素是一个数据域;
#define NumberOfMyNodes 10 //存储区中存储块数量"节点数量"
#define SizeOfMyNode 4 //每个存储块大小为4,即节点的大小为4
//由于一个指针变量占用4字节所以块的大小一定要为4的倍数
//而且必须大于等于一个指针变量(4字节)占用的空间,否则的话存储块创建不成功
//STM32F10xxx内置64K字节的静态SRAM,它可以以字节,半字(16位)或全字(32位)访问
//SRAM的起始地址是0x20000000
__align(4) u8 MyChainTable[NumberOfMyNodes][SizeOfMyNode] __attribute__((at(0x20001000)));
//定义一个数组,用作链表
typedef struct os_mem
{
void *OSMemAddr; /*根节点地址*/
void *OSMemFreeList;/*下一个节点的地址*/
INT32U OSMemBlkSize;/*节点的字节数*/
INT32U OSMemNBlks; /*节点的总数量”*/
INT32U OSMemNFree;/*可用节点数量*/
#if OS_MEM_NAME_EN > 0u
INT8U *OSMemName; /*指向链表名字的指针*/
#endif
}OS_MEM;
OS_MEM *OSMemFreeList;
//函数功能:将addr[nblks][ blksize]生成链表,并将链表地址返回
//addr为根节点地址
//nblks为节点的总数量
//blksize为节点的字节数
OS_MEM *OSMemCreate (void *addr,INT32U nblks,INT32U blksize,INT8U *perr)
{
OS_MEM *pmem;
INT8U *pblk;
void **plink;
INT32U loops;
INT32U i;
#if OS_CRITICAL_METHOD == 3u
OS_CPU_SR cpu_sr = 0u;
#endif
///检查开始
#ifdef OS_SAFETY_CRITICAL
if (perr == (INT8U *)0)
{//perr指针为0
OS_SAFETY_CRITICAL_EXCEPTION();
}
#endif
#ifdef OS_SAFETY_CRITICAL_IEC61508
if (OSSafetyCriticalStartFlag == OS_TRUE)
{
OS_SAFETY_CRITICAL_EXCEPTION();
}
#endif
#if OS_ARG_CHK_EN > 0u
if (addr == (void *)0)
{//检查到”连续存储块”的首地址为0
*perr = OS_ERR_MEM_INVALID_ADDR;
return ((OS_MEM *)0);
}
if ( ( (INT32U)addr & ( sizeof(void *) - 1u) ) != 0u )
{//若sizeof(void *)=4,则addr不满足4字节对齐
*perr = OS_ERR_MEM_INVALID_ADDR;
return ((OS_MEM *)0);
}
if (nblks < 2u)
{//”节点的总数量”至少是2,否则链表没有意义
*perr = OS_ERR_MEM_INVALID_BLKS;
return ((OS_MEM *)0);
}
if (blksize < sizeof(void *))
{//节点是由指针域和数据域组成;
//若sizeof(void *)=4,则节点字节数必须大于等于4
*perr = OS_ERR_MEM_INVALID_SIZE;
return ((OS_MEM *)0);
}
#endif
OS_ENTER_CRITICAL();//进入临界区(无法被中断打断),需要定义cpu_sr变量
pmem = OSMemFreeList;
//因为他们都是指针,修改pmem就等同修改OSMemFreeList
// OSMemFreeList是uCOSii创建的OS_MEM结构指针
if (OSMemFreeList != (OS_MEM *)0)
{//查看系统是否声明过” OSMemFreeList结构指针”变量
OSMemFreeList = (OS_MEM *)OSMemFreeList->OSMemFreeList;
//下一个节点的地址
}
OS_EXIT_CRITICAL();//退出临界区(可以被中断打断)
if (pmem == (OS_MEM *)0)
{//检查到pmem结构指针为0
*perr = OS_ERR_MEM_INVALID_PART;
return ((OS_MEM *)0);
}
///检查结束
plink = (void **)addr;
// addr为根节点地址
//plink是void**型指针的指针, 给plink指针赋值
pblk = (INT8U *)addr;
// addr为根节点地址
//pblk是字节型指针, 给pblk指针赋值
loops = nblks - 1u;
//nblks为节点的总数量
for (i = 0u; i < loops; i++)
{
pblk +=blksize;
//blksize为节点的字节数
//修改” pblk指针”,使它指向”下一个节点”的首地址
*plink = (void *)pblk;
//pblk是字节型指针,使用(void *)将该指针强制转换为void*型指针
// plink为当前节点的首地址
//给当前节点的指针域赋值,记录下一个节点的首地址
plink = (void **)pblk;
//pblk是字节型指针,使用(void **)将该指针强制转换为void**型指针
//给plink指针赋值, 使plink指向下一个节点的首地址
}
*plink = (void *)0;
//给最后一个节点的指针域赋值,记录下一个节点的首地址为0
pmem->OSMemAddr= addr; //根节点地址
pmem->OSMemFreeList = addr; //下一个节点的地址”
pmem->OSMemNFree= nblks; //可用节点数量
pmem->OSMemNBlks = nblks; //节点的总数量
pmem->OSMemBlkSize = blksize;//节点的字节数
*per = OS_ERR_NONE;
return (pmem);//返回pmem指针
}
//pmem为OS_MEM结构指针,用来管理链表操作
//per=OS_ERR_NONE,表示申请到内存
//返回值为申请到节点的首地址,不等于0,表示申请到一个节点
//注意:OSMemGet()执行一次,只能申请一个节点
//由于节点的数据长度是固定的,这一点和malloc()不同
//ptr=OSMemGet(INTERNAL_MEM_Pointer,&err);
void *OSMemGet (OS_MEM *pmem,INT8U *perr)
{
void *pblk;
#if OS_CRITICAL_METHOD == 3u
OS_CPU_SR cpu_sr = 0u;
#endif
#ifdef OS_SAFETY_CRITICAL
if (perr == (INT8U *)0)
{//perr指针为0
OS_SAFETY_CRITICAL_EXCEPTION();
}
#endif
#if OS_ARG_CHK_EN > 0u
if (pmem == (OS_MEM *)0)
{//pmem指针为0
*perr = OS_ERR_MEM_INVALID_PMEM;
return ((void *)0);
}
#endif
OS_ENTER_CRITICAL();
if(pmem->OSMemNFree > 0u)
{//可用节点数量大于0
pblk = pmem->OSMemFreeList;// 读取”下一个节点”的地址
pmem->OSMemFreeList = *(void **)pblk;//读取”下下一个节点”的地址
pmem->OSMemNFree--;//可用节点数量减1
OS_EXIT_CRITICAL();
*perr = OS_ERR_NONE;
return (pblk); //申请到内存
}
//可用节点数量等于0//
OS_EXIT_CRITICAL();
*perr = OS_ERR_MEM_NO_FREE_BLKS;
return ((void *)0);//没有申请到内存
}
//函数功能:释放地址为pblk的数据块,使它再次变为可用的节点
//pmem为OS_MEM结构指针,用来管理链表操作
//pblk为释放节点的首地址
//OSMemPut(INTERNAL_MEM_Pointer,ptr);
//注意: OSMemPut()执行一次,只释放一个节点
//这一点和free()用法差不多,只要知道内存首地址就可以释放内存;
//通常后申请的节点先释放,先申请的节点后释放
INT8U OSMemPut (OS_MEM *pmem, void *pblk)
{
#if OS_CRITICAL_METHOD == 3u
OS_CPU_SR cpu_sr = 0u;
#endif
#if OS_ARG_CHK_EN > 0u
if (pmem == (OS_MEM *)0)
{//pmem指针为0
return (OS_ERR_MEM_INVALID_PMEM);
}
if (pblk == (void *)0)
{//pblk指针为0
return (OS_ERR_MEM_INVALID_PBLK);
}
#endif
OS_ENTER_CRITICAL();
if (pmem->OSMemNFree >= pmem->OSMemNBlks)
{//pmem->OSMemNFree可用节点数量
//pmem->OSMemNBlks节点的总数量
OS_EXIT_CRITICAL();
return (OS_ERR_MEM_FULL);
}
*(void **)pblk= pmem->OSMemFreeList;
//读取”下一个节点”的地址,用这个地址去覆盖当前释放节点的”指针域”
//将这个释放的块插入到”可用的节点”中间
pmem->OSMemFreeList = pblk;//更新”下一个节点”的地址
pmem->OSMemNFree++; //可用节点数量加1
OS_EXIT_CRITICAL();
return (OS_ERR_NONE);
}
typedef struct os_mem_data
{
void *OSAddr; //根节点地址
void *OSFreeList; //可用的下一个节点的地址
INT32U OSBlkSize; //节点的字节数
INT32U OSNBlks; //节点的总数量
INT32U OSNFree; //可用节点数量
INT32U OSNUsed; //已用的节点数量
}OS_MEM_DATA;
//pmem为OS_MEM结构指针,用来管理链表操作
INT8U OSMemQuery (OS_MEM *pmem, OS_MEM_DATA *p_mem_data)
{
#if OS_CRITICAL_METHOD == 3u
OS_CPU_SR cpu_sr = 0u;
#endif
#if OS_ARG_CHK_EN > 0u
if (pmem == (OS_MEM *)0)
{
return (OS_ERR_MEM_INVALID_PMEM);
}
if (p_mem_data == (OS_MEM_DATA *)0)
{
return (OS_ERR_MEM_INVALID_PDATA);
}
#endif
OS_ENTER_CRITICAL();
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; //可用节点数量
OS_EXIT_CRITICAL();
p_mem_data->OSNUsed = p_mem_data->OSNBlks - p_mem_data->OSNFree;
//已用的节点数量
return (OS_ERR_NONE);
}
#include "MEMORY_Task.h"
#include "stdio.h" //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "My_Task_Priority.h"
void PrintMyChainTable(void);
void MEMORY_Task(void *pdata);
const char MEMORY_Task_rn_REG[]="\r\n";
const char MEMORY_Task_Initialise_REG[]="MEMORY_Task Initialise";
/*
MyRootNode=0x20001000
AddressOfMyNodes=0x20001000, AddressOfMyNextNode=0x20001020, MyChainTable[0][0]=0x20001020
AddressOfMyNodes=0x20001020, AddressOfMyNextNode=0x20001040, MyChainTable[1][0]=0x20001040
AddressOfMyNodes=0x20001040, AddressOfMyNextNode=0x20001060, MyChainTable[2][0]=0x20001060
AddressOfMyNodes=0x20001060, AddressOfMyNextNode=0x20001080, MyChainTable[3][0]=0x20001080
AddressOfMyNodes=0x20001080, AddressOfMyNextNode=0x200010a0, MyChainTable[4][0]=0x200010a0
AddressOfMyNodes=0x200010a0, AddressOfMyNextNode=0x200010c0, MyChainTable[5][0]=0x200010c0
AddressOfMyNodes=0x200010c0, AddressOfMyNextNode=0x200010e0, MyChainTable[6][0]=0x200010e0
AddressOfMyNodes=0x200010e0, AddressOfMyNextNode=0x20001100, MyChainTable[7][0]=0x20001100
AddressOfMyNodes=0x20001100, AddressOfMyNextNode=0x20001120, MyChainTable[8][0]=0x20001120
AddressOfMyNodes=0x20001120, AddressOfMyNextNode=0x0, MyChainTable[9][0]=0x0
*/
void PrintMyChainTable(void)
{
u32 i;
u32 *p;
printf("%s",MEMORY_Task_rn_REG);
p=(u32*)&MyChainTable[0][0];
printf("MyRootNode=0x%p",p);
for (i=0;i { p=(u32*)&MyChainTable[i][0]; printf("%s",MEMORY_Task_rn_REG); printf("AddressOfMyNodes=0x%p, AddressOfMyNextNode=0x%x, MyChainTable[%u][0]=0x%x",p,*p,i,*p); } } /* 执行测试结果如下: Address_0x20001000=0x12345678 Address_0x20001004=0x87654321 MyRootNode=0x20001000 AddressOfMyNodes=0x20001000, AddressOfMyNextNode=0x12345678, MyChainTable[0][0]=0x12345678,占用指针域,保存数据 AddressOfMyNodes=0x20001004, AddressOfMyNextNode=0x87654321, MyChainTable[1][0]=0x87654321,占用指针域,保存数据 AddressOfMyNodes=0x20001008, AddressOfMyNextNode=0x2000100c, MyChainTable[2][0]=0x2000100c AddressOfMyNodes=0x2000100c, AddressOfMyNextNode=0x20001010, MyChainTable[3][0]=0x20001010 AddressOfMyNodes=0x20001010, AddressOfMyNextNode=0x0, MyChainTable[4][0]=0x0 MyRootNode=0x20001000 AddressOfMyNodes=0x20001000, AddressOfMyNextNode=0x20001004, MyChainTable[0][0]=0x20001004 AddressOfMyNodes=0x20001004, AddressOfMyNextNode=0x20001008, MyChainTable[1][0]=0x20001008 AddressOfMyNodes=0x20001008, AddressOfMyNextNode=0x2000100c, MyChainTable[2][0]=0x2000100c AddressOfMyNodes=0x2000100c, AddressOfMyNextNode=0x20001010, MyChainTable[3][0]=0x20001010 AddressOfMyNodes=0x20001010, AddressOfMyNextNode=0x0, MyChainTable[4][0]=0x0 */ //MEMORY_Task任务 void MEMORY_Task(void *pdata) { u8 err,cnt; u8 *ptr1,*ptr2; printf("%s",MEMORY_Task_rn_REG); printf("%s",MEMORY_Task_Initialise_REG); while(1) { cnt++; if(cnt>100) { ptr1 = OSMemGet(INTERNAL_MEM_Pointer,&err); //相当于C语言中的malloc() //假如从未被申请过,ptr1和根节点地址相等 ptr2 = OSMemGet(INTERNAL_MEM_Pointer,&err); //相当于C语言中的malloc() if(ptr1!=NULL) *(u32*)ptr1=0x12345678; if(ptr2!=NULL) *(u32*)ptr2=0x87654321; if(ptr1!=NULL && ptr2!=NULL) { printf("%s",MEMORY_Task_rn_REG); printf("Address_0x%p=0x%x ",ptr1,*(u32*)ptr1); printf("Address_0x%p=0x%x",ptr2,*(u32*)ptr2); PrintMyChainTable(); } if(ptr2!=NULL)//后申请的先释放 { OSMemPut(INTERNAL_MEM_Pointer,ptr2); //释放一个内存块,首地址为ptr2 //相当于C语言中的free() ptr2=NULL; } if(ptr1!=NULL)//先申请的后释放 { OSMemPut(INTERNAL_MEM_Pointer,ptr1); //释放一个内存块,首地址为ptr1 //相当于C语言中的free() ptr1=NULL; } PrintMyChainTable(); printf("%s",MEMORY_Task_rn_REG); cnt=0; } OSTimeDlyHMSM(0,0,0,1000);//延时1000ms } }