详解UCOS中的内存管理
详解UCOS中的内存管理
在嵌入式设备中,持续的调用malloc()和free()容易产生内存碎片,长时间的运行最终会导致内存消耗殆尽。UCOS提供了一套内存管理机制,在系统初始化的时候就分配好内存空间,将所有可用的空间组织成链表,需要申请内存的时候直接从链表中申请,释放内存的时候直接将内存归还到空余内存链表中即可。使用这种方法不仅避免了内存碎片的产生,而且使得在常数时间内分配内存空间成为可能。
UCOS中内存管理的结构体是OS_MEM,具体结构如下:
- typedef struct os_mem //内存控制块
- {
- void *OSMemAddr; //指向内存分区的首地址
- void *OSMemFreeList; //该内存分区的block链表的表头
- INT32U OSMemBlkSize; //每一个block的大小
- INT32U OSMemNBlks; //该分区中block的数目
- INT32U OSMemNFree; //该分区中空闲block的数目
- } OS_MEM;
在UCOS中,一个内存分区被划分成很多个大小相等的内存块,这些内存块链接成链表,链表的表头存于OSMemFreeList中。
比如一个有4个内存块,每个内存块128bit的内存分区Memoy如下:
INT32U Memory[4][4]
UCOS的思路是我们将要分配的空间组织成二维数组,然后二维数组的每一行就是一个block,二维数组的列数既是block的大小。
Memory的第一个block的首地址是 Memor[0]
Memory的第二个block的首地址是 Memory[1]
Memory的第三个block的首地址是 Memory[2]
Memory的第四个block的首地址是 Memory[3]
为了管理方便我们在每一个block的开头存储下一个block的地址,这样就把所有的block串接成了单链表。
第一个block的开头存储 Memory[1]
第一个block的开头存储 Memory[2]
第一个block的开头存储 Memory[3]
第一个block的开头存储 NULL
最终结果如下图:
UCOS中创建内存分区的核心代码如下(代码取自OSMemCreate):
- //内存分区的首地址
- plink = (void **)addr;
- //第二个block的地址
- pblk = (INT8U *)((INT32U)addr + blksize);
- for (i = 0; i < (nblks - 1); i++)
- //每一个block的开头存放下一个block的首地址
- *plink = (void *)pblk;
- //更新指针而已
- plink = (void **)pblk;
- pblk = (INT8U *)((INT32U)pblk + blksize);
- }
- //最后一个block指向NULL
- *plink = (void *)0;
申请一个内存块:
- //还有内存可以分配
- if (pmem->OSMemNFree > 0)
- {
- 取一个block
- pblk = pmem->OSMemFreeList;
- //使链表表头指向这个block的下一个block
- pmem->OSMemFreeList = *(void **)pblk;
- //更新内存块的数量
- pmem->OSMemNFree--;
- }
删除一个内存块:
- //在将要删除的内存块的首地址存放block表头的地址
- *(void **)pblk = pmem->OSMemFreeList;
- //更新链表表头
- pmem->OSMemFreeList = pblk;
- //更新可用内存块的数目
- pmem->OSMemNFree++;