COSII 採用的是固定分區的方式管理內存。
將連續大塊的內存按分區來管理,每個系統中若干個這樣的分區,每個分區中又有若干個大小相同的內存塊。
在進行內存分配的時候,根據需求從分區中得到幾個內存塊。而在釋放的時候,內存塊又重新返回所在的分區。
1:內存管理的數據結構
內存管理的數據結構包括有內存控制塊(MCB),空閒內存控制塊鏈表,內存分區等。
其定義如下:
typedef struct os_mem { /* MEMORY CONTROL BLOCK */
void *OSMemAddr; //內存分區的起始地址
void *OSMemFreeList; //空閒塊鏈表地址
INT32U OSMemBlkSize; //每個內存塊的大小
INT32U OSMemNBlks; //內存塊的數量
INT32U OSMemNFree; //空閒內存塊的數量
#if OS_MEM_NAME_EN > 0u
INT8U *OSMemName; //名稱
#endif
} OS_MEM;
內存控制塊(MCB)是內存分區的核心數據塊。每一個內存分區對應一個MCB。
MCB的實體在ucos_ii.h中定義全局變量,系統默認的數目是5個
#define OS_MAX_MEM_PART 5
OS_EXT OS_MEM OSMemTbl[OS_MAX_MEM_PART]
所有的未用的MCB,都會鏈接在一個鏈表上,並且用OSMemFreeList指針指向該鏈表表頭。
內存分區需要用戶提供,一大塊連續地址的內存。
通常我們只需要定義一個二維數組就可以瞭如 INT32U MemBuf[10][20],這個分區就是10*20*4=800字節大小的內存分區,分爲10個內存塊,每個內存塊80個字節。
2:內存管理函數
內存管理函數主要有5個
2.1:內存控制塊初始化OS_MemInit
在系統初始化調用OSInit的時候,若是配置內存管理模塊,OSInit會調用OS_MemInit
該函數的主要功能是將所有內存控制塊的數據清零,並且將所有MCB內存控制塊連在空閒鏈表上。
2.2:創建內存分區OSMemCreate
內存分區在操作系統初始化的時候並不存在。在使用一個分區之前,必須先定義一個二維數組,但是這個二維數組還沒有進行分區。
OSMemCreate就是使用一個MCB,對這個二維數組進行管理,形成一個分區。
函數的原型如下:OS_MEM *OSMemCreate (void *addr, INT32U nblks, INT32U blksize,INT8U *perr)
addr是內存分區的首地址,nblk是分區數目,blksize是一個分區的大小,perr調用的過程的信息
其函數的主要實現代碼如下:
//從空閒鏈表上取MCB
pmem = OSMemFreeList;
if (OSMemFreeList != (OS_MEM *)0) { /* See if pool of free partitions was empty */
OSMemFreeList = (OS_MEM *)OSMemFreeList->OSMemFreeList;
}
OS_EXIT_CRITICAL();
if (pmem == (OS_MEM *)0) { /* See if we have a memory partition */
*perr = OS_ERR_MEM_INVALID_PART;
return ((OS_MEM *)0);
}
//開始進行分區管理
plink = (void **)addr; //獲取內存分區的首地址
pblk = (INT8U *)addr; //獲取塊的第一個地址
loops = nblks - 1u;
for (i = 0u; i < loops; i++) {
pblk += blksize; //pblk指向下一個內存塊
*plink = (void *)pblk; //當前塊的第一個元素中存儲下一個塊的地址
plink = (void **)pblk; //plink指向下一個塊
}
*plink = (void *)0; //達到最後一個內存塊
//根據分區數據,設置MCB
pmem->OSMemAddr = addr; /* Store start address of memory partition */
pmem->OSMemFreeList = addr; /* Initialize pointer to pool of free blocks */
pmem->OSMemNFree = nblks; /* Store number of free blocks in MCB */
pmem->OSMemNBlks = nblks;
pmem->OSMemBlkSize = blksize; /* Store block size of each memory blocks */
*perr = OS_ERR_NONE;
2.3:內存分區的獲取內存塊OSMemGet
該函數的主要功能是通過已經創建的內存分區中獲取內存塊。若申請成功,則返回內存地址,若申請不成功,則返回空地址。
該函數的原型如下:void *OSMemGet (OS_MEM *pmem, INT8U *perr)
其主要實現代碼如下:
if (pmem->OSMemNFree > 0u) { //是否有空閒的內存塊
pblk = pmem->OSMemFreeList; //取空閒內存塊鏈表的表頭
pmem->OSMemFreeList = *(void **)pblk;
pmem->OSMemNFree--; //空閒內存塊數量減1
OS_EXIT_CRITICAL();
*perr = OS_ERR_NONE; /* No error */
return (pblk); /* Return memory block to caller */
}
2.4:釋放內存塊OSMemPut
當任務不在需要該內存塊的時候,就將其歸還給之前的內存分區。
函數原型如下 INT8U OSMemPut (OS_MEM *pmem, void *pblk) pblk爲內存塊的地址
if (pmem->OSMemNFree >= pmem->OSMemNBlks) { //確保內存塊沒有全部釋放掉,也即是說空閒快數量沒有大於後者等於最大空閒快數量
OS_EXIT_CRITICAL();
return (OS_ERR_MEM_FULL);
}
*(void **)pblk = pmem->OSMemFreeList; //將內存塊插入到鏈表表頭
pmem->OSMemFreeList = pblk;
pmem->OSMemNFree++; //空閒內存塊的數量加1
2.5:查詢分區狀態OSMemQuery
該函數的原型如下:INT8U OSMemQuery (OS_MEM *pmem, OS_MEM_DATA *p_mem_data)
其主要功能是將分區當前的信息(都在MCB中),讀取存入p_mem_data指向的結構體中。