ucos
系統(tǒng)由于構(gòu)思巧妙,結(jié)構(gòu)精簡設(shè)計(jì),可讀性強(qiáng),同時又具有實(shí)時性操作系統(tǒng)大部分的優(yōu)點(diǎn),在
物聯(lián)網(wǎng)開發(fā)中應(yīng)用非常廣泛。
之前一直都只是會用ucos
卻沒有好好研究過它,最近項(xiàng)目中要用到了
ucos-II
所以順便研究了一番,突然發(fā)現(xiàn)
ucos-II
的內(nèi)存管理寫得非常巧妙。
廢話不多說,直接上代碼:
先看一個內(nèi)存塊結(jié)構(gòu)體
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
指向一塊內(nèi)存的起始地址;
OSMemFreeList
指向一個可利用的空塊的地址;
再看看內(nèi)存的分配函數(shù)
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 }
這個函數(shù)把addr
指向的一塊連續(xù)的內(nèi)存劈成
nblks
個
blksize
大小的內(nèi)存塊。里面利用了一個技巧:在
60-64
行的這個
for
循環(huán)里面把每個內(nèi)存塊的首地址保存在它的上一個內(nèi)存塊的首地址空間里面。
67
行把
pmem
里面的
OSMemFreeList
指向第一個空塊的地址
(
當(dāng)前空塊的第一個地址保存的是下一個空塊的地址
)
。
再看看內(nèi)存獲取函數(shù)
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 }
這個函數(shù)除了紅色標(biāo)出來的這幾行代碼其它都不重要,在這幾行代碼里面獲取OSMemFreeList
指向的內(nèi)存塊的地址之后把這個地址
return
出去使用。
那么問題來了:為什么沒有遍歷空塊的過程?
原因就在于上面所提到的那種巧妙地方法,每個內(nèi)存塊的前32
位保存的是下一個空塊的地址,在獲取到
OSMemFreeList
后
OSMemFreeList
指針應(yīng)該要指向下一個空塊,而下一個空塊就在當(dāng)前獲取到的空塊的第一個地址里面,所以在
26
行把
OSMemFreeList
指針指向了當(dāng)前獲取到的內(nèi)存塊的第一個地址所指向的內(nèi)存塊處。
最后就是內(nèi)存釋放函數(shù)
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 }
同樣,最關(guān)鍵的代碼在紅色部分。首先把要釋放的內(nèi)存塊的首地址指向OSMemFreeList
指向的地址
(
這個地址就是下一個空塊的首地址
)
,之后把
OSMemFreeList
指向剛剛釋放的內(nèi)存塊的首地址。只用了一行代碼就把一個要釋放的內(nèi)存塊插入了空塊鏈表,再調(diào)整
OSMemFreeList
指針指向剛釋放的內(nèi)存塊,就是這么簡單粗暴?。?!
一句話總結(jié):ucos-II
的內(nèi)存管理利用內(nèi)存塊的首地址保存下一個空塊的首地址的方式把所有空塊鏈接起來。至少有一個好處,那就是在獲取內(nèi)存塊的時候少了一個對整個內(nèi)存塊進(jìn)行遍歷找出空塊的過程!
來源:博客園