μC/OS-II--内存管理
作者:互联网
在μC/OS-II中,将大块内存作为一个分区,在系统中有多个分区,每个分区又分为整数个大小相同的内存块,由于大小相同,分配和释放时间相同,应用程序根据需要从不同的分区得到不同的内存块,内存块释放时,放回以前的位置。
内存控制块
为了跟踪每个内存分区,引入内存控制块进行管理。OSMemAddr为指向内存分区起始地址的指针,当调用OSMemCreate时初始化,之后不能修改。OSMemFreeList为指向下一个空余内存控制块或下一个空余内存块的指针。OSMemBlkSize在建立该内存分区时建立,表示内存分区中单个内存块的大小。OSMemNBlks在建立该内存分区时建立,表示内存分区中总的内存块数量。OSMemNFree为内存分区中当前空余内存块数量。
typedef struct os_mem {
void *OSMemAddr; //指向内存分区起始地址的指针
void *OSMemFreeList; // 内存指向下一个空余内存块的指针
INT32U OSMemBlkSize; //内存分区中内存块的大小
INT32U OSMemNBlks; // 内存分区中总的内存块数量
INT32U OSMemNFree; // 内存分区中当前空余内存块数量
#if OS_MEM_NAME_SIZE > 1
INT8U OSMemName[OS_MEM_NAME_SIZE]; //分区名
#endif
} OS_MEM;//分区控制块结构体
内存块管理初始化
#if (OS_MEM_EN > 0) && (OS_MAX_MEM_PART > 0)
OS_EXT OS_MEM *OSMemFreeList; //指向空闲内存分区的指针
OS_EXT OS_MEM OSMemTbl[OS_MAX_MEM_PART];//内存块表
#endif
void OS_MemInit (void)
{
#if OS_MAX_MEM_PART == 1//内存分区大小为1
OS_MemClr((INT8U *)&OSMemTbl[0], sizeof(OSMemTbl)); //清空所有内存控制块区域
OSMemFreeList = (OS_MEM *)&OSMemTbl[0];//将OSMemFreeList指向OSMemTbl第一个内存控制块
#if OS_MEM_NAME_SIZE > 1
OSMemFreeList->OSMemName[0] = '?'; //内存分区名初始化为未知名
OSMemFreeList->OSMemName[1] = OS_ASCII_NUL;
#endif
#endif
#if OS_MAX_MEM_PART >= 2//内存分区大小大于2
OS_MEM *pmem;
INT16U i;
OS_MemClr((INT8U *)&OSMemTbl[0], sizeof(OSMemTbl)); //清空所有内存控制块区域
pmem = &OSMemTbl[0]; //将OSMemFreeList指向OSMemTbl第一个内存控制块
for (i = 0; i < (OS_MAX_MEM_PART - 1); i++) {
pmem->OSMemFreeList = (void *)&OSMemTbl[i+1]; //将所有内存控制块通过OSMemFreeList指针链接
#if OS_MEM_NAME_SIZE > 1
pmem->OSMemName[0] = '?'; //内存分区名初始化为未知名
pmem->OSMemName[1] = OS_ASCII_NUL;
#endif
pmem++;//指向下一个内存控制块
}
pmem->OSMemFreeList = (void *)0; //最后一个内存控制块的OSMemFreeList为空
#if OS_MEM_NAME_SIZE > 1
pmem->OSMemName[0] = '?'; //内存分区名初始化为未知名
pmem->OSMemName[1] = OS_ASCII_NUL;
#endif
OSMemFreeList = &OSMemTbl[0]; //将OSMemFreeList指向OSMemTbl第一个内存控制块
#endif
}
首先判断分区数量是否为1,如果是,清空内存控制块,将OSMemFreeList指向OSMemTbl第一个内存控制块,然后分区名是否大于1,大于1的话,初始化分区名为未知名。若分区数量大于等于2,如果是,清空内存控制块,将pmem指向OSMemTbl第一个内存控制块,将所有内存控制块通过OSMemFreeList指针链接,所有分区初始化分区名为未知名,最后一个内存控制块的OSMemFreeList指向空,OSMemFreeList指向OSMemTbl第一个内存控制块。
创建内存分区
在使用一个内存分区之前,必须建立该内存分区,使用OSMemCreate创建内存分区,函数共四个参数,内存分区的起始地址addr,分区内内存块总数nblks,每个内存块的字节数blksize,指向出错信息的指针perr。
OS_MEM *OSMemCreate (void *addr, INT32U nblks, INT32U blksize, INT8U *perr)
{
OS_MEM *pmem;
INT8U *pblk;
void **plink;
INT32U i;
#if OS_CRITICAL_METHOD == 3 //为CPU状态寄存器分配内存
OS_CPU_SR cpu_sr = 0;
#endif
#if OS_ARG_CHK_EN > 0
if (perr == (INT8U *)0) { //传进来的perr为空,不正确,返回空内存控制块地址
return ((OS_MEM *)0);
}
if (addr == (void *)0) { //传进来的addr为空,不正确,返回空内存控制块地址,并且返回错误类型OS_ERR_MEM_INVALID_ADDR,表示无效地址
*perr = OS_ERR_MEM_INVALID_ADDR;
return ((OS_MEM *)0);
}
if (((INT32U)addr & (sizeof(void *) - 1)) != 0){ //地址对齐,为4的倍数
*perr = OS_ERR_MEM_INVALID_ADDR;
return ((OS_MEM *)0);
}
if (nblks < 2) { //分区数至少两个,返回空内存控制块地址,并且返回错误类型OS_ERR_MEM_INVALID_BLKS,表示无效地址
*perr = OS_ERR_MEM_INVALID_BLKS;
return ((OS_MEM *)0);
}
if (blksize < sizeof(void *)) { //必须使单个内存块大小至少能存一个指针,因为同一个分区内存块通过链表串联
*perr = OS_ERR_MEM_INVALID_SIZE;
return ((OS_MEM *)0);
}
#endif
OS_ENTER_CRITICAL();
pmem = OSMemFreeList; //pmem指向空闲内存控制块
if (OSMemFreeList != (OS_MEM *)0) { //指向下一个空闲内存控制块
OSMemFreeList = (OS_MEM *)OSMemFreeList->OSMemFreeList;
}
OS_EXIT_CRITICAL();
if (pmem == (OS_MEM *)0) { //确定是否有一个内存分区
*perr = OS_ERR_MEM_INVALID_PART;
return ((OS_MEM *)0);
}
plink = (void **)addr; //plink为指向指针的指针,将addr进行强制转换
pblk = (INT8U *)((INT32U)addr + blksize); //获取第二个区块的起始地址
for (i = 0; i < (nblks - 1); i++) {
*plink = (void *)pblk; //指向下一个内存块地址
plink = (void **)pblk; //指向指针的指针后移一个内存块
pblk = (INT8U *)((INT32U)pblk + blksize); //获得下一个区块起始地址
}
*plink = (void *)0; //最后一个内存块指向为空
pmem->OSMemAddr = addr; //填充起始地址
pmem->OSMemFreeList = addr; //填充空闲地址
pmem->OSMemNFree = nblks; //填充可用内存块数
pmem->OSMemNBlks = nblks; //填充总内存块数
pmem->OSMemBlkSize = blksize; //填充每个内存块的大小
*perr = OS_ERR_NONE; //返回无错误
return (pmem);
}
首先为CPU状态寄存器分配内存,检查perr是否为空,空的话返回空内存控制块地址,检查addr是否正确,不正确返回空的内存控制块地址,并且返回错误类型OS_ERR_MEM_INVALID_ADDR,即无效地址。检查分区数是否大于两个,如果不是,返回空地址指针,并且返回错误类型OS_ERR_MEM_INVALID_BLKS,即无效的块数量,必须使单个内存块大小至少能存一个指针,因为同一个分区内存块通过链表串联,否则返回空地址指针,并且返回错误类型OS_ERR_MEM_INVALID_SIZE,即无效的块大小。检查完成后,通过OSMemFreeList获取一个空闲块,并将OSMemFreeList指向下一个空闲快,然后检查获得的空闲块是否存在,如果不存在,返回空的内存控制块地址,并且返回错误类型OS_ERR_MEM_INVALID_PART,即无效分区。然后将内存空间中的所有内存块连接成单向链表。plink为指向指针的指针,pblk为指针,指向各个内存块首地址,首先将plink指向第一个内存块的指针域,pblk赋值为第二个内存块指针域的值,最后循环做若下操作,将plink指向的指针域指向下一个内存块指针域,plink指向下一个内存块指针域,然后将下一个内存块指针域的值赋值给pblk,最后一个内存块指向为空。最后填充起始地址,空闲地址,可用内存块数,总内存块数,每个内存块的大小,返回无错误的perr状态。
OSMemCreate函数创建分区完成后,内存控制块与对应的内存分区,分区内的内存块之间的关系如下:
分配一个内存块
调用OSMemGet函数,从已经建立的内存分区中申请一个内存块,传入参数为pmem,指向特定分区内存控制块的指针,指向出错信息的指针perr,返回可用内存块地址。
void *OSMemGet (OS_MEM *pmem, INT8U *perr)
{
void *pblk;
#if OS_CRITICAL_METHOD == 3 //为CPU状态寄存器分配内存
OS_CPU_SR cpu_sr = 0;
#endif
#if OS_ARG_CHK_EN > 0
if (perr == (INT8U *)0) { //传进来的perr为空,不正确,返回空地址
return ((void *)0);
}
if (pmem == (OS_MEM *)0) { //传进来对应的分区内存控制块为不存在,返回空地址,返回错误信息OS_ERR_MEM_INVALID_PMEM
*perr = OS_ERR_MEM_INVALID_PMEM;
return ((void *)0);
}
#endif
OS_ENTER_CRITICAL();
if (pmem->OSMemNFree > 0) { //检查该分区中是否有可用的内存块
pblk = pmem->OSMemFreeList; //如果是,pllk指向下一个可用内存块
pmem->OSMemFreeList = *(void **)pblk; //调整下一个可用内存块地址
pmem->OSMemNFree--; //可用内存块数量减一
OS_EXIT_CRITICAL();
*perr = OS_ERR_NONE; //无错误
return (pblk); //返回可用内存苦熬地址
}
OS_EXIT_CRITICAL();
*perr = OS_ERR_MEM_NO_FREE_BLKS; // 返回无空余内存块
return ((void *)0); //返回空指针
}
首先为CPU状态寄存器分配内存,检查传进来的perr是否为空,是的话返回空地址,判断传进来对应的分区内存控制块是否不存在,不存在返回空地址,返回错误信息OS_ERR_MEM_INVALID_PMEM,即无效的分区内存控制块指针。然后检查该分区中是否有可用的内存块,没有的话返回OS_ERR_MEM_NO_FREE_BLKS,即无空余内存块,返回空指针。如果有可用内存块,如果是,pllk指向可用内存块,调整下一个可用内存块地址,可用内存块数量减一,perr返回无错误,返回可用内存块地址。
释放一个内存块
当不在使用内存块时,使用函数OSMemPut释放这个内存块,放回到相应的内存分区中。传入参数为特定分区内存控制块指针pmem,内存块指针pblk。
INT8U OSMemPut (OS_MEM *pmem, void *pblk)
{
#if OS_CRITICAL_METHOD == 3 //为CPU状态寄存器分配内存
OS_CPU_SR cpu_sr = 0;
#endif
#if OS_ARG_CHK_EN > 0
if (pmem == (OS_MEM *)0) { //检查是否是一个有效的内存控制块指针
return (OS_ERR_MEM_INVALID_PMEM);
}
if (pblk == (void *)0) { //检查是否是一个有效的内存块地址
return (OS_ERR_MEM_INVALID_PBLK);
}
#endif
OS_ENTER_CRITICAL();
if (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);
}
首次为CPU状态寄存器分配内存,检查是否是一个有效的内存控制块指针,检查是否是一个有效的内存块地址。检查内存块全未使用,是的话返回OS_ERR_MEM_FULL,空闲内存块已满。将归还的内存空间插入空余内存块链表的表头,分区中剩余内存块数量加1,返回无错误。
查询内存分区状态
可以使用OSMemQuery函数查询特定分区的有关信息,通过该函数能知道内存分区内存块大小,可用内存块数目,已经使用了的内存块数量。
typedef struct os_mem_data {
void *OSAddr; //指向内存分区首地址的指针
void *OSFreeList; //指向空闲内存块链表首地址的指针
INT32U OSBlkSize; //每个内存块所含的字节数
INT32U OSNBlks; //内存分区总的内存块数
INT32U OSNFree; //空闲内存块总数
INT32U OSNUsed; //正在使用的内存块总数
} OS_MEM_DATA;
#if OS_MEM_QUERY_EN > 0
INT8U OSMemQuery (OS_MEM *pmem, OS_MEM_DATA *p_mem_data)
{
#if OS_CRITICAL_METHOD == 3 //为CPU状态寄存器分配内存
OS_CPU_SR cpu_sr = 0;
#endif
#if OS_ARG_CHK_EN > 0
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);
}
#endif
首先,为CPU状态寄存器分配内存,检查是否是一个有效的内存控制块指针,是否是一个有效的内存信息单元,填充内存分区首地址,空闲内存块链表首地址,每个内存块所含的字节数,内存分区总的内存块数,空闲内存块总数,正在使用的内存块总数。
另外还有获取和设置内存分区名的函数
INT8U OSMemNameGet (OS_MEM *pmem, INT8U *pname, INT8U *perr)
void OSMemNameSet (OS_MEM *pmem, INT8U *pname, INT8U *perr)
标签:OSMemFreeList,MEM,分区,II,内存,OS,pmem 来源: https://blog.csdn.net/qq_15391889/article/details/90302001