COSII 采用的是固定分区的方式管理内存。
将连续大块的内存按分区来管理,每个系统中若干个这样的分区,每个分区中又有若干个大小相同的内存块。
在进行内存分配的时候,根据需求从分区中得到几个内存块。而在释放的时候,内存块又重新返回所在的分区。
1:内存管理的数据结构
内存管理的数据结构包括有内存控制块(MCB),空闲内存控制块链表,内存分区等。
其定义如下:
typedef struct os_mem { /* MEMORY CONTROL BLOCK */
void *OSMemAddr; //内存分区的起始地址
void *OSMemFreeList; //空闲块链表地址
INT32U OSMemBlkSize; //每个内存块的大小
INT32U OSMemNBlks; //内存块的数量
INT32U OSMemNFree; //空闲内存块的数量
#if OS_MEM_NAME_EN > 0u
INT8U *OSMemName; //名称
#endif
} OS_MEM;
内存控制块(MCB)是内存分区的核心数据块。每一个内存分区对应一个MCB。
MCB的实体在ucos_ii.h中定义全局变量,系统默认的数目是5个
#define OS_MAX_MEM_PART 5
OS_EXT OS_MEM OSMemTbl[OS_MAX_MEM_PART]
所有的未用的MCB,都会链接在一个链表上,并且用OSMemFreeList指针指向该链表表头。
内存分区需要用户提供,一大块连续地址的内存。
通常我们只需要定义一个二维数组就可以了如 INT32U MemBuf[10][20],这个分区就是10*20*4=800字节大小的内存分区,分为10个内存块,每个内存块80个字节。
2:内存管理函数
内存管理函数主要有5个
2.1:内存控制块初始化OS_MemInit
在系统初始化调用OSInit的时候,若是配置内存管理模块,OSInit会调用OS_MemInit
该函数的主要功能是将所有内存控制块的数据清零,并且将所有MCB内存控制块连在空闲链表上。
2.2:创建内存分区OSMemCreate
内存分区在操作系统初始化的时候并不存在。在使用一个分区之前,必须先定义一个二维数组,但是这个二维数组还没有进行分区。
OSMemCreate就是使用一个MCB,对这个二维数组进行管理,形成一个分区。
函数的原型如下:OS_MEM *OSMemCreate (void *addr, INT32U nblks, INT32U blksize,INT8U *perr)
addr是内存分区的首地址,nblk是分区数目,blksize是一个分区的大小,perr调用的过程的信息
其函数的主要实现代码如下:
//从空闲链表上取MCB
pmem = OSMemFreeList;
if (OSMemFreeList != (OS_MEM *)0) { /* See if pool of free partitions was empty */
OSMemFreeList = (OS_MEM *)OSMemFreeList->OSMemFreeList;
}
OS_EXIT_CRITICAL();
if (pmem == (OS_MEM *)0) { /* See if we have a memory partition */
*perr = OS_ERR_MEM_INVALID_PART;
return ((OS_MEM *)0);
OSMemFreeList = (OS_MEM *)OSMemFreeList->OSMemFreeList;
}
OS_EXIT_CRITICAL();
if (pmem == (OS_MEM *)0) { /* See if we have a memory partition */
*perr = OS_ERR_MEM_INVALID_PART;
return ((OS_MEM *)0);
}
//开始进行分区管理
plink = (void **)addr; //获取内存分区的首地址
pblk = (INT8U *)addr; //获取块的第一个地址
loops = nblks - 1u;
for (i = 0u; i < loops; i++) {
for (i = 0u; i < loops; i++) {
pblk += blksize; //pblk指向下一个内存块
*plink = (void *)pblk; //当前块的第一个元素中存储下一个块的地址
plink = (void **)pblk; //plink指向下一个块
}
*plink = (void *)0; //达到最后一个内存块
//根据分区数据,设置MCB
pmem->OSMemAddr = addr; /* Store start address of memory partition */
pmem->OSMemFreeList = addr; /* Initialize pointer to pool of free blocks */
pmem->OSMemNFree = nblks; /* Store number of free blocks in MCB */
pmem->OSMemNBlks = nblks;
pmem->OSMemBlkSize = blksize; /* Store block size of each memory blocks */
pmem->OSMemFreeList = addr; /* Initialize pointer to pool of free blocks */
pmem->OSMemNFree = nblks; /* Store number of free blocks in MCB */
pmem->OSMemNBlks = nblks;
pmem->OSMemBlkSize = blksize; /* Store block size of each memory blocks */
*perr = OS_ERR_NONE;
2.3:内存分区的获取内存块OSMemGet
该函数的主要功能是通过已经创建的内存分区中获取内存块。若申请成功,则返回内存地址,若申请不成功,则返回空地址。
该函数的原型如下:void *OSMemGet (OS_MEM *pmem, INT8U *perr)
其主要实现代码如下:
if (pmem->OSMemNFree > 0u) { //是否有空闲的内存块
pblk = pmem->OSMemFreeList; //取空闲内存块链表的表头
pmem->OSMemFreeList = *(void **)pblk;
pmem->OSMemNFree--; //空闲内存块数量减1
OS_EXIT_CRITICAL();
*perr = OS_ERR_NONE; /* No error */
return (pblk); /* Return memory block to caller */
*perr = OS_ERR_NONE; /* No error */
return (pblk); /* Return memory block to caller */
}
2.4:释放内存块OSMemPut
当任务不在需要该内存块的时候,就将其归还给之前的内存分区。
函数原型如下 INT8U OSMemPut (OS_MEM *pmem, void *pblk) pblk为内存块的地址
if (pmem->OSMemNFree >= pmem->OSMemNBlks) { //确保内存块没有全部释放掉,也即是说空闲快数量没有大于后者等于最大空闲快数量
OS_EXIT_CRITICAL();
return (OS_ERR_MEM_FULL);
}
return (OS_ERR_MEM_FULL);
}
*(void **)pblk = pmem->OSMemFreeList; //将内存块插入到链表表头
pmem->OSMemFreeList = pblk;
pmem->OSMemNFree++; //空闲内存块的数量加1
2.5:查询分区状态OSMemQuery
该函数的原型如下:INT8U OSMemQuery (OS_MEM *pmem, OS_MEM_DATA *p_mem_data)
其主要功能是将分区当前的信息(都在MCB中),读取存入p_mem_data指向的结构体中。