近來學習了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 指向該內存塊,最後將可用內存塊數目加一。