ucos系統因爲構思巧妙,結構精簡設計,可讀性強,同時又具備實時性操做系統大部分的優勢,在嵌入式產品中應用很是普遍。less
以前一直都只是會用ucos卻沒有好好研究過它,最近項目中要用到了ucos-II因此順便研究了一番,忽然發現ucos-II的內存管理寫得很是巧妙。函數
廢話很少說,直接上代碼:oop
先看一個內存塊結構體this
1 typedef struct os_mem { /* MEMORY CONTROL BLOCK */ 2 void *OSMemAddr; /* Pointer to beginning of memory partition */ 3 void *OSMemFreeList; /* Pointer to list of free memory blocks */ 4 INT32U OSMemBlkSize; /* Size (in bytes) of each block of memory */ 5 INT32U OSMemNBlks; /* Total number of blocks in this partition */ 6 INT32U OSMemNFree; /* Number of memory blocks remaining in this partition */ 7 #if OS_MEM_NAME_EN > 0u 8 INT8U *OSMemName; /* Memory partition name */ 9 #endif 10 } OS_MEM;
其中 OSMemAddr指向一塊內存的起始地址;spa
OSMemFreeList指向一個可利用的空塊的地址;操作系統
再看看內存的分配函數設計
1 OS_MEM *OSMemCreate (void *addr, 2 INT32U nblks, 3 INT32U blksize, 4 INT8U *perr) 5 { 6 OS_MEM *pmem; 7 INT8U *pblk; 8 void **plink; 9 INT32U loops; 10 INT32U i; 11 #if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */ 12 OS_CPU_SR cpu_sr = 0u; 13 #endif 14 15 16 17 #ifdef OS_SAFETY_CRITICAL 18 if (perr == (INT8U *)0) { 19 OS_SAFETY_CRITICAL_EXCEPTION(); 20 } 21 #endif 22 23 #ifdef OS_SAFETY_CRITICAL_IEC61508 24 if (OSSafetyCriticalStartFlag == OS_TRUE) { 25 OS_SAFETY_CRITICAL_EXCEPTION(); 26 } 27 #endif 28 29 #if OS_ARG_CHK_EN > 0u 30 if (addr == (void *)0) { /* Must pass a valid address for the memory part.*/ 31 *perr = OS_ERR_MEM_INVALID_ADDR; 32 return ((OS_MEM *)0); 33 } 34 if (((INT32U)addr & (sizeof(void *) - 1u)) != 0u){ /* Must be pointer size aligned */ 35 *perr = OS_ERR_MEM_INVALID_ADDR; 36 return ((OS_MEM *)0); 37 } 38 if (nblks < 2u) { /* Must have at least 2 blocks per partition */ 39 *perr = OS_ERR_MEM_INVALID_BLKS; 40 return ((OS_MEM *)0); 41 } 42 if (blksize < sizeof(void *)) { /* Must contain space for at least a pointer */ 43 *perr = OS_ERR_MEM_INVALID_SIZE; 44 return ((OS_MEM *)0); 45 } 46 #endif 47 OS_ENTER_CRITICAL(); 48 pmem = OSMemFreeList; /* Get next free memory partition */ 49 if (OSMemFreeList != (OS_MEM *)0) { /* See if pool of free partitions was empty */ 50 OSMemFreeList = (OS_MEM *)OSMemFreeList->OSMemFreeList; 51 } 52 OS_EXIT_CRITICAL(); 53 if (pmem == (OS_MEM *)0) { /* See if we have a memory partition */ 54 *perr = OS_ERR_MEM_INVALID_PART; 55 return ((OS_MEM *)0); 56 } 57 plink = (void **)addr; /* Create linked list of free memory blocks */ 58 pblk = (INT8U *)addr; 59 loops = nblks - 1u; 60 for (i = 0u; i < loops; i++) { 61 pblk += blksize; /* Point to the FOLLOWING block */ 62 *plink = (void *)pblk; /* Save pointer to NEXT block in CURRENT block */ 63 plink = (void **)pblk; /* Position to NEXT block */ 64 } 65 *plink = (void *)0; /* Last memory block points to NULL */ 66 pmem->OSMemAddr = addr; /* Store start address of memory partition */ 67 pmem->OSMemFreeList = addr; /* Initialize pointer to pool of free blocks */ 68 pmem->OSMemNFree = nblks; /* Store number of free blocks in MCB */ 69 pmem->OSMemNBlks = nblks; 70 pmem->OSMemBlkSize = blksize; /* Store block size of each memory blocks */ 71 *perr = OS_ERR_NONE; 72 return (pmem); 73 }
這個函數把addr指向的一塊連續的內存劈成nblks個blksize大小的內存塊。裏面利用了一個技巧:在60-64行的這個for循環裏面把每一個內存塊的首地址保存在它的上一個內存塊的首地址空間裏面。67行把pmem裏面的OSMemFreeList指向第一個空塊的地址(當前空塊的第一個地址保存的是下一個空塊的地址)。指針
再看看內存獲取函數code
1 void *OSMemGet (OS_MEM *pmem, 2 INT8U *perr) 3 { 4 void *pblk; 5 #if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */ 6 OS_CPU_SR cpu_sr = 0u; 7 #endif 8 9 10 11 #ifdef OS_SAFETY_CRITICAL 12 if (perr == (INT8U *)0) { 13 OS_SAFETY_CRITICAL_EXCEPTION(); 14 } 15 #endif 16 17 #if OS_ARG_CHK_EN > 0u 18 if (pmem == (OS_MEM *)0) { /* Must point to a valid memory partition */ 19 *perr = OS_ERR_MEM_INVALID_PMEM; 20 return ((void *)0); 21 } 22 #endif 23 OS_ENTER_CRITICAL(); 24 if (pmem->OSMemNFree > 0u) { /* See if there are any free memory blocks */ 25 pblk = pmem->OSMemFreeList; /* Yes, point to next free memory block */ 26 pmem->OSMemFreeList = *(void **)pblk; /* Adjust pointer to new free list */ 27 pmem->OSMemNFree--; /* One less memory block in this partition */ 28 OS_EXIT_CRITICAL(); 29 *perr = OS_ERR_NONE; /* No error */ 30 return (pblk); /* Return memory block to caller */ 31 } 32 OS_EXIT_CRITICAL(); 33 *perr = OS_ERR_MEM_NO_FREE_BLKS; /* No, Notify caller of empty memory partition */ 34 return ((void *)0); /* Return NULL pointer to caller */ 35 }
這個函數除了紅色標出來的這幾行代碼其它都不重要,在這幾行代碼裏面獲取OSMemFreeList指向的內存塊的地址以後把這個地址return出去使用。blog
那麼問題來了:爲何沒有遍歷空塊的過程?
緣由就在於上面所提到的那種巧妙地方法,每一個內存塊的前32位保存的是下一個空塊的地址,在獲取到OSMemFreeList後OSMemFreeList指針應該要指向下一個空塊,而下一個空塊就在當前獲取到的空塊的第一個地址裏面,因此在26行把OSMemFreeList指針指向了當前獲取到的內存塊的第一個地址所指向的內存塊處。
最後就是內存釋放函數
1 INT8U OSMemPut (OS_MEM *pmem, 2 void *pblk) 3 { 4 #if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */ 5 OS_CPU_SR cpu_sr = 0u; 6 #endif 7 8 9 10 #if OS_ARG_CHK_EN > 0u 11 if (pmem == (OS_MEM *)0) { /* Must point to a valid memory partition */ 12 return (OS_ERR_MEM_INVALID_PMEM); 13 } 14 if (pblk == (void *)0) { /* Must release a valid block */ 15 return (OS_ERR_MEM_INVALID_PBLK); 16 } 17 #endif 18 OS_ENTER_CRITICAL(); 19 if (pmem->OSMemNFree >= pmem->OSMemNBlks) { /* Make sure all blocks not already returned */ 20 OS_EXIT_CRITICAL(); 21 return (OS_ERR_MEM_FULL); 22 } 23 *(void **)pblk = pmem->OSMemFreeList; /* Insert released block into free block list */ 24 pmem->OSMemFreeList = pblk; 25 pmem->OSMemNFree++; /* One more memory block in this partition */ 26 OS_EXIT_CRITICAL(); 27 return (OS_ERR_NONE); /* Notify caller that memory block was released */ 28 }
一樣,最關鍵的代碼在紅色部分。首先把要釋放的內存塊的首地址指向OSMemFreeList指向的地址(這個地址就是下一個空塊的首地址),以後把OSMemFreeList指向剛剛釋放的內存塊的首地址。只用了一行代碼就把一個要釋放的內存塊插入了空塊鏈表,再調整OSMemFreeList指針指向剛釋放的內存塊,就是這麼簡單粗暴!!!
一句話總結:ucos-II的內存管理利用內存塊的首地址保存下一個空塊的首地址的方式把全部空塊連接起來。至少有一個好處,那就是在獲取內存塊的時候少了一個對整個內存塊進行遍歷找出空塊的過程!