一、前言
在嵌入式系统中,时常会面临Ram受限的情况。所以用C lib里的,malloc()和 free()来申请和释放内存时,频繁的内存请求造成的内存碎片会对系统性能造成负面影响。Ucos的解决办法是为内存划分不同大小的内存区域,每个区域内有一定数量、相同大小的内存块。每次申请内存都必须要以一个内存块为单位,释放内存块时,该内存块会回到相应的内存分区。这样,确实,内存的碎片的问题得到了一定程度的解决。
然而,这意味着内存管理对编码的人来说,是不透明的。牺牲灵活性的代价就是用户必须提前根据自己的实际需要,把空闲内存划分成不同的内存分区,再把内存分区切割成几个相同大小的内存块。即使是在系统运行过程中的动态内存请求,编码的人也要提前为自己不同的任务分配内存区域和内存块。UCOS的使用者必须纵观整个项目,构建出最合适的memory map,才能高效使用ram资源。
二、内存控制块
MCB用来定义一个内存区域的属性。这种有点类似面向对象的控制结构在UCOSii里很常见,只要在Tcb、Ecb和Mcb这样的控制块中,加入一些方法,这个控制块瞬间就变成了一个类。只是出于实时性的需要,RTOS一般使用效率更高的结构性语言而经常与高级语言无缘,但UCOSii作者的设计思想依然很值得我们借鉴。
MCB原型:
typedef struct { void *OSMemAddr; void *OSMemFreeList; INT32U OSMemBlkSize; INT32U OSMemNBlks; INT32U OSMemNFree; } OS_MEM;1234567
.OSMemAddr 是指向内存分区起始地址的指针。它在建立内存分区时被初始化,在此之后就不能更改了。
.OSMemFreeList 是指向下一个空闲内存控制块或者下一个空闲的内存块的指针。
.OSMemBlkSize和.OSMemNBlks 是内存分区中内存的总大小和内存块数量,是用户建立该内存分区时指定的。
.OSMemNFree 是内存分区中当前可以得空闲内存块数量。
不得不说这个数据结构十分简洁,减掉一个属性和加一个属性都显得没有必要。
三、相关函数
3.1 建立一个内存分区,OSMemCreate()
在建立一个内存分区前,用户需要自己先在全局定义好这个分区和一个内存控制块指针。
OS_MEM *CommTxBuf; INT8U CommTxPart[100][32];12
然后将定义好的区域作为参数传给OSMemCreate(),使用指针接收函数的返回值。
CommTxBuf = OSMemCreate(CommTxPart, 100, 32, &err);1
这个函数最主要的功能,就是初始化一个Mcb结构。将起始地址、总大小、分块数量、初始空闲内存等等写入一个Mcb,然后这个Mcb的指针作为函数的返回值。之后对该块内存的申请、释放操作都将通过这个指针来进行,和Ecb完全一样。
这里值得一提的是空闲内存块表,它是一个指针单向链表,Freelist指向它的第一个元素。初始化代码如下:
plink = (void **)addr; pblk = (INT8U *)addr + blksize; for (i = 0; i < (nblks - 1); i++) { *plink = (void *)pblk; plink = (void **)pblk; pblk= pblk + blksize; } *plink = (void *)0;12345678
为了修改指针的值,使用了二级指针。将一个二级指针指向的指针的值先修改成下一个内存块的起始地址,然后将该二级指针的指向下一个内存块的起始地址,然后循环,以此构建一个指针链表。最后一个指针指向NULL。
3.2 分配一个内存块,OSMemGet()
如果内存分区没有被申请完,OSMemGet()返回一个内存区域里的一个空闲块。将空闲链表里的第一个元素删除即可,其实就是修改.OSMemFreeList的向,将它指向空闲块链表第二个元素就OK。
3.3 释放一个内存块,OSMemPut()
释放内存时,OSMemPut()将改内存块的指针放回空闲队列。将.OSMemFreeList的值指向它,再将第一个元素指向原链表头。这样可以观察到,对于内存的动态申请和释放,UCOSii用的实际上是先进先出的策略。最后被释放的内存块将会最先被分配给申请者。
3.4 查询一个内存分区的状态,OSMemQuery()
返回Mcb当前的各种属性。
四、用信号量等待一块内存
UCOSii没有提供相应的机制,但可以用信号量模拟内存申请的等待,申请内存前先申请信号量。
例子:
for (;;) { OSSemPend(SemaphorePtr, 0, &err); pblock = OSMemGet(PartitionPtr, &err); OSMemPut(PartitionPtr, pblock); OSSemPost(SemaphorePtr); }
上一篇:UCOSii(六)——移植
下一篇:UCOSii(四)——任务的通信与同步