01. 为什么不使用动态内存分配?
1)由于分配算法的复杂度及堆空间的使用情况,内存分配的时间具有不确定性。
2)在不断进行的内存分配及释放的过程中,产生内存碎片,导致可分配的内存变少
因此UCOS实现了自己的内存管理方式,以解决malloc方式进行动态内存分配的碎片化内存分配时间不确定的问题。
02. ucos 内存管理的使用
2.1 创建并初始化内存分区
1) 定义一块静态内存以及一个内存分区控制块(OS_MEM)
2) 调用OSMemCreate() 将分配的静态内存组织成一个单向链表,并将指向链表开头的指针存储在 OS_MEM 结构中。
OS_MEM MyPartition;
CPU_INT08U MyPartitionStorage[12][100];
void main (void)
{
OS_ERR err;
:
:
OSInit(&err);
:
OSMemCreate((OS_MEM *)&MyPartition,
(CPU_CHAR *)"My Partition",
(void *)&MyPartitionStorage[0][0],
(OS_MEM_QTY ) 12,
(OS_MEM_SIZE)100,
(OS_ERR *)&err);
/* Check 'err' */
}
2.2 从内存分区获取内存块
1)调用OSMemGet(),从内存分区中获取一个内存块,返回已配内存块的指针
2)必须检查返回的错误代码,以确保调用OSMemGet成功
OS_MEM MyPartition;
CPU_INT08U *MyDataBlkPtr;
void MyTask (void *p_arg)
{
OS_ERR err;
MyDataBlkPtr = (CPU_INT08U *)OSMemGet((OS_MEM *)&MyPartition,
(OS_ERR *)&err);
if (err == OS_ERR_NONE) {
/* You have a memory block from the partition */
}
}
2.3 把内存块放回内存分区
1)调用OSMemPut(),把内存块放入内存分区中
2)必须检查返回的错误代码,以确保调用OSMemPut成功
OS_MEM MyPartition;
CPU_INT08U *MyDataBlkPtr;
void MyTask (void *p_arg)
{
OS_ERR err;
OSMemPut((OS_MEM *)&MyPartition,
(void *)MyDataBlkPtr,
(OS_ERR *)&err);
if (err == OS_ERR_NONE) {
/* You properly returned the memory block to the partition */
}
}
03. ucos 内存管理的实现
3.1 OS_MEM 内存分区控制块
struct os_mem {
CPU_CHAR *NamePtr;
void *AddrPtr; /* Pointer to beginning of memory partition */
void *FreeListPtr; /* Pointer to list of free memory blocks */
OS_MEM_SIZE BlkSize; /* Size (in bytes) of each block of memory */
OS_MEM_QTY NbrMax; /* Total number of blocks in this partition */
OS_MEM_QTY NbrFree; /* Number of memory blocks remaining in this partition */
};
3.2 创建内存分区
p_mem 应用程序定义的内存分区控制块指针,通过mallo分区或静态分配
p_name 内存分区的名称
p_addr 内存分区的起始地址
n_blks 内存块的数量
blk_size 内存块的大小
p_err 错误代码指针
void OSMemCreate (OS_MEM *p_mem,
CPU_CHAR *p_name,
void *p_addr,
OS_MEM_QTY n_blks,
OS_MEM_SIZE blk_size,
OS_ERR *p_err)
{
OS_MEM_QTY i;
OS_MEM_QTY loops;
CPU_INT08U *p_blk;
void **p_link;
CPU_SR_ALLOC();
p_link = (void **)p_addr; /* Create linked list of free memory blocks */
p_blk = (CPU_INT08U *)p_addr;
loops = n_blks - 1u;
for (i = 0u; i < loops; i++) {
p_blk += blk_size;
*p_link = (void *)p_blk; /* Save pointer to NEXT block in CURRENT block */
p_link = (void **)(void *)p_blk; /* Position to NEXT block */
}
*p_link = (void *)0; /* Last memory block points to NULL */
CPU_CRITICAL_ENTER();
p_mem->AddrPtr = p_addr; /* Store start address of memory partition */
p_mem->FreeListPtr = p_addr; /* Initialize pointer to pool of free blocks */
p_mem->NbrFree = n_blks; /* Store number of free blocks in MCB */
p_mem->NbrMax = n_blks;
p_mem->BlkSize = blk_size; /* Store block size of each memory blocks */
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_NONE;
}
3.3 从内存分区获取内存块
p_mem 应用程序定义的内存分区控制块指针
p_err 错误代码指针
void *OSMemGet (OS_MEM *p_mem,
OS_ERR *p_err)
{
void *p_blk;
CPU_SR_ALLOC();
CPU_CRITICAL_ENTER();
if (p_mem->NbrFree == 0u) { /* See if there are any free memory blocks */
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_MEM_NO_FREE_BLKS; /* No, Notify caller of empty memory partition */
return ((void *)0); /* Return NULL pointer to caller */
}
p_blk = p_mem->FreeListPtr; // 从空闲内存块链表的表头取出一个内存块
p_mem->FreeListPtr = *(void **)p_blk; //空闲内存块链表指针指向下一个内存块
//空闲内存块减一
p_mem->NbrFree--;
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_NONE; /* No error */
return (p_blk); /* Return memory block to caller */
3.4 把内存块放回内存分区
p_mem 应用程序定义的内存分区控制块指针
p_blk 内存块指针
p_err 错误代码指针
void OSMemPut (OS_MEM *p_mem,
void *p_blk,
OS_ERR *p_err)
{
CPU_SR_ALLOC();
CPU_CRITICAL_ENTER();
if (p_mem->NbrFree >= p_mem->NbrMax) {
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_MEM_FULL;
return;
}
//把内存块插入空闲内存块链表表头
*(void **)p_blk = p_mem->FreeListPtr;
p_mem->FreeListPtr = p_blk;
//空闲内存块加一
p_mem->NbrFree++;
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_NONE;
}
04. ucos内存管理的优缺点
优点:内存分配速度快,没有内存碎片
缺点: 一次只能分配一块固定大小的内存,内存分配灵活性差