近来学习了UCOS-II嵌入式操作系统,感慨颇多。
首先是系统麻雀虽小,五脏俱全,然后是不管讲的如何简单,都应该看一下代码。
下面将相关的东西总结一下:
UCOS对内存进行两级管理,即将连续的内存空间分成若干个内存分区,一个内存分区里面又有若干个大小相等的内存块。操作系统对内存分区为单位对内存进行管理,任务以内存块为单位来获得和释放动态内存。而内存分区和内存块的使用情况则是通过内存控制块来进行记录。
内存控制块:
typedef struct os_mem {
void *OSMemAddr;//当前内存分区的起始地址
void *OSMemFreeList;//**内存块和内存分区的链表指针**
INT32U OSMemBlkSize;//当前内存分区的内存块大小
INT32U OSMemNBlks;//当前内存分区中内存块的数目
INT32U OSMemNFree;//当前内存分区中未被使用的内存块数目
} OS_MEM;
上述的指针OSMemFreeList需要特别注意一下。
首先系统在ucos_ii.h中已经声明了如下相关内容:
extern OS_MEM *OSMemFreeList;//内存分区链表头指针
extern OS_MEM OSMemTbl[OS_MAX_MEM_PART];//内存分区数组
然后动态内存分配主要涉及以下这些函数:
- OS_MemInit(void)
- OS_MEM *OSMemCreate (void *addr,INT32U nblks,INT32U blksize,INT8U *perr)
- void *OSMemGet (OS_MEM *pmem,INT8U *perr)
- INT8U OSMemPut (OS_MEM *pmem,void *pblk)
这些函数分别涉及了动态内存的初始化,内存分区的创建,内存块的申请和释放
下面逐个进行讲解:
1.OS_MemInit(void)
通过这个函数,系统将上面的所有空内存控制块连接成为一个链表,OSMemFreeList指向链表头(这是个结构体指针,不要和该结构体的成员混淆,因为两者名字相同)。
执行完效果图如下:
2.OS_MEM *OSMemCreate (void *addr,INT32U nblks,INT32U blksize,INT8U *perr)
该函数分配一个内存分区,并且将里面的内存块建立成一个链表。
OS_MEM *pmem;
INT8U *pblk;
void **plink;
pmem = OSMemFreeList;
if (OSMemFreeList != (OS_MEM *)0)
{
OSMemFreeList = (OS_MEM *)OSMemFreeList->OSMemFreeList;
}
plink = (void **)addr;
pblk = (INT8U *)addr;
loops = nblks - 1;
for (i = 0u; i < loops; i++)
{
pblk += blksize;
*plink = (void *)pblk;
plink = (void **)pblk;
}//这一段是对内存块建立链表的过程,如果不太懂得话请看一下我前面的指针和指针的指针的讲解
*plink = (void *)0;
pmem->OSMemAddr = addr;
pmem->OSMemFreeList = addr;
pmem->OSMemNFree = nblks;
pmem->OSMemNBlks = nblks;
pmem->OSMemBlkSize = blksize;
*perr = OS_ERR_NONE;
return (pmem);
操作完成后效果如下所示:
每一个内存块的起始地址对应的内存空间中存放了下一个内存块的地址。
使用实例:
OSMemCreate (&A,6,8,&perr);
这样类似创建了一个A[6][8] 矩阵的内存分区,该内存分区里面有6个内存块,每个内存块的大小为8.
3. void *OSMemGet (OS_MEM *pmem,INT8U *perr)
if (pmem->OSMemNFree > 0u)
{
pblk = pmem->OSMemFreeList;
pmem->OSMemFreeList = *(void **)pblk;
pmem->OSMemNFree--;
}
这个函数用于从前面生成的内存分区中取得一个内存块。
由上图可以看出,在创建了内存分区之后,该分区内存控制块结构体的OSMemAddr和OSMemFreeList 都指向分区的起始地址,而且每个内存块的首地址中都存放了下一个内存块的地址,因此上述OSMemFreeList 也就是内存块链表的头指针。在申请一个内存块时首先取得未被分配的内存块链的首个内存块地址(即OSMemFreeList 指向的地址 ),然后在将该地址中存放的内容–也就是下个内存块的起始地址赋给OSMemFreeList,最后将可用的内存块数量减一。
4. INT8U OSMemPut (OS_MEM *pmem,void *pblk)
*(void **)pblk = pmem->OSMemFreeList;
pmem->OSMemFreeList = pblk;
pmem->OSMemNFree++;
这个函数用于释放一个内存块到其所属的内存分区中,与上述的申请操作正好相反。首先将待释放的内存块的首地址对应的内存空间中存放进下一个内存块的地址,然后将OSMemFreeList 指向该内存块,最后将可用内存块数目加一。