uCos的内存管理
作為一個(gè)操作系統(tǒng)內(nèi)核,必須有向用戶提供申請(qǐng)和釋放內(nèi)存的服務(wù),uCos作為一個(gè)實(shí)時(shí)操作系統(tǒng)也不例外。
內(nèi)存的動(dòng)態(tài)申請(qǐng)和釋放在嵌入式編程中經(jīng)常用到,比如我們需要給另外一個(gè)任務(wù)發(fā)送一個(gè)消息,我們就可以在發(fā)送消息前,申請(qǐng)(OSMemGet )一個(gè)內(nèi)存塊,然后把這個(gè)內(nèi)存塊作為消息發(fā)送出去(OSQPost ),消息被處理完后,內(nèi)存塊釋放(OSMemPut)掉,但如果不用內(nèi)存塊來(lái)存放消息內(nèi)容,比如用一個(gè)全局的數(shù)組,也是可以實(shí)現(xiàn)這樣的效果的,但這樣做,管理起來(lái)會(huì)麻煩點(diǎn),發(fā)送消息的時(shí)間和數(shù)量是不確定的,那么可能需要定義多個(gè)這樣的數(shù)組,而且這些數(shù)組需要進(jìn)行互斥訪問(wèn),事實(shí)上,uCos已經(jīng)幫我們做好了這些管理,我們只需要使用這些服務(wù)就可以了,而且這些服務(wù)質(zhì)量是很高滴。
uCos的內(nèi)存服務(wù)不同于我們使用malloc或mfree那樣可以申請(qǐng)和釋放任意大小的內(nèi)存塊,由于嵌入式軟件的特殊性,程序里需要申請(qǐng)的內(nèi)存塊大小和數(shù)量是可以預(yù)先估計(jì)和計(jì)算的,換句話說(shuō),我們根本不需要分配任意大小的內(nèi)存塊,我們只需要申請(qǐng)本應(yīng)用中確定的幾種內(nèi)存塊就足夠了,比如我們?cè)诔绦蛑卸x串口通訊的數(shù)據(jù)包大小是32個(gè)字節(jié),則我們?cè)谌蝿?wù)間傳遞這樣的串口數(shù)據(jù)包時(shí),需要用的內(nèi)存塊大小就可以固定為32字節(jié),當(dāng)然在PC中,這不行,因?yàn)橛脩舻膽?yīng)用程序是不可預(yù)知的,在嵌入式程序中,則是可行的。
uCos的內(nèi)存服務(wù)正如上面所說(shuō)的,我們可以向uCos申請(qǐng)一些特定大小的內(nèi)存塊,當(dāng)然這些內(nèi)存塊的大小是我們可以設(shè)定的,我們首先告訴uCos我們需要的內(nèi)存塊大小為多少,以及這樣的內(nèi)存塊我們最多需要多少個(gè),然后uCos幫我們建立和管理起來(lái),以后申請(qǐng)和釋放這個(gè)特定大小的內(nèi)存塊了。
先介紹下uCos的內(nèi)存管理的程序模型,這個(gè)模型事實(shí)上不復(fù)雜,甚至很簡(jiǎn)單,但簡(jiǎn)單并意味著簡(jiǎn)陋。
uCos的內(nèi)存服務(wù)使用實(shí)例如下:
INT8U?? Mem_Of_Buff[100][32];
OS_MEM ?*Mem_Buff;
void uCos_mem_test(void)
{
INT8U err;
INT8U?p_got_mem;
/*?創(chuàng)建一個(gè)內(nèi)存分區(qū)Mem_Buff,創(chuàng)建后該內(nèi)存分區(qū)里將有100個(gè)大小為32字節(jié)的內(nèi)存塊?*/
Mem_Buff?= OSMemCreate(Mem_Of_Buff, 100, 32, &err);
/*?從內(nèi)存分區(qū)Mem_Buff中申請(qǐng)一個(gè)內(nèi)存塊?*/
p_got_mem?= (INT8U *)OSMemGet(Mem_Buff,&err);
/*把之前申請(qǐng)到的內(nèi)存塊釋放回內(nèi)存分區(qū)Mem_Buff */
OSMemPut (Mem_Buff,p_got_mem);
}
從上面的例子中,可以看出要使用uCos的內(nèi)存管理服務(wù)很簡(jiǎn)單,事實(shí)上,uCos的內(nèi)存管理服務(wù)提供的函數(shù)總共才4個(gè),她們分別如下:
OS_MEM? *OSMemCreate (void *addr, INT32U nblks, INT32U blksize, INT8U *err);
void? *OSMemGet (OS_MEM *pmem, INT8U *err);
INT8U? OSMemPut (OS_MEM? *pmem, void *pblk);
INT8U? OSMemQuery (OS_MEM *pmem, OS_MEM_DATA *pdata);
void??OS_MemInit (void);---?這個(gè)函數(shù)用戶不能調(diào)用
這里又要特別注意,OS_MemInit這個(gè)函數(shù)是由OSInit這個(gè)函數(shù)調(diào)用的,用戶不能也不需要主動(dòng)調(diào)用OS_MemInit函數(shù)。下面我們仔細(xì)分析這幾個(gè)函數(shù)來(lái)學(xué)習(xí)uCos的內(nèi)存管理是怎么實(shí)現(xiàn)的。
uCos的內(nèi)存管理程序的編程大概思路可概況如下:uCos用一個(gè)結(jié)構(gòu)體OS_MEM來(lái)管理一個(gè)內(nèi)存分區(qū),同時(shí)根據(jù)用戶的配置定義了一個(gè)元素類型為OS_MEM的數(shù)組OSMemTbl來(lái)存放所有的OS_MEM的結(jié)構(gòu)體,同時(shí)這個(gè)OSMemTbl數(shù)組里的每個(gè)元素用鏈表連接起來(lái),當(dāng)用戶建立一個(gè)分區(qū)時(shí),則從該鏈表里取出一個(gè)元素來(lái)管理新建立的分區(qū)。所以我們調(diào)用OSMemCreate來(lái)建立一個(gè)新的內(nèi)存分區(qū)時(shí),返回的是一個(gè)指向OS_MEM的指針,實(shí)際上,返回的就是這個(gè)內(nèi)存管理控制塊的其中某一塊的首地址。
內(nèi)存管理控制塊的鏈表結(jié)構(gòu)如下:
?
如上圖所示,OSMemFreeList是一個(gè)全局變量,他指向鏈表頭結(jié)點(diǎn)。OS_MAX_MEM_PART是一個(gè)供用戶配置的宏,所以用戶建立的內(nèi)存分區(qū)數(shù)不能超過(guò)OS_MAX_MEM_PART這個(gè)值,否則建立分區(qū)失敗。這里特別注意OS_MEM結(jié)構(gòu)體里的OSMemFreeList成員,該成員在該內(nèi)存管理控制塊未被使用前是用來(lái)指向下一個(gè)空閑的內(nèi)存管理控制塊,但當(dāng)該控制塊被用于管理一個(gè)特定的分區(qū)后,OSMemFreeList將被用戶指向該分區(qū)的下一個(gè)空閑內(nèi)存塊。
在調(diào)用OSMemCreate建立分區(qū)時(shí),uCos會(huì)將該分區(qū)里的每個(gè)內(nèi)存塊通過(guò)鏈表連接起來(lái),同時(shí)把內(nèi)存管理控制塊的OSMemFreeList成員指向該鏈表的頭結(jié)點(diǎn)。
當(dāng)用戶申請(qǐng)一個(gè)內(nèi)存塊時(shí),就從該鏈表里取出一個(gè)元素,當(dāng)用戶釋放一個(gè)內(nèi)存塊時(shí),就把該內(nèi)存塊添加到鏈表里。從上圖可以看出,每個(gè)內(nèi)存塊的啟始地址存放的是下一個(gè)空閑內(nèi)存塊的起始地址,uCos在這里巧妙利用了內(nèi)存塊本身來(lái)構(gòu)建鏈表,使得內(nèi)存的使用效率大大提高,由此也可以看出,每個(gè)內(nèi)存塊的大小至少應(yīng)該能存放一個(gè)指針。而且由于內(nèi)存分區(qū)里的每個(gè)內(nèi)存塊的首地址被用來(lái)存放一個(gè)指針,所以在內(nèi)存分區(qū)里的內(nèi)存必須在調(diào)用OSMemGet獲得該內(nèi)存后,才能進(jìn)行操作,不允許直接操作內(nèi)存分區(qū)里的內(nèi)存塊,盡管我們可以直接對(duì)內(nèi)存分區(qū)里的每個(gè)內(nèi)存塊進(jìn)行操作。
*******************************************************
ucos-II的內(nèi)存管理提供了對(duì)某塊完整內(nèi)存的修改,功能非常簡(jiǎn)單NBNC的代碼大概只有200多行,在整個(gè)內(nèi)核中也基本是獨(dú)立的。它通過(guò)如下方式對(duì)內(nèi)存塊進(jìn)行管理:
OS_MEM *mem; INT8U buff[16][128];void main(void) {INT8U err; mem = OSMemCreate(&buff[0][0],16,128,&err); }下文分析os_mem.c文件,從全局變量的定義與接口函數(shù)的實(shí)現(xiàn)兩方面記錄ucos-II的內(nèi)存管理機(jī)制。
1. MEM的全局變量
OS_EXT OS_MEM *OSMemFreeList; /* Pointer to free list of memory partitions */ OS_EXT OS_MEM OSMemTbl[OS_MAX_MEM_PART]; /* Storage for memory partition manager */上面兩個(gè)全局變量在內(nèi)核初始化中由OS_MemInit初始,用戶試圖進(jìn)行管理的一個(gè)內(nèi)存塊,如上例中的buff,對(duì)應(yīng)內(nèi)核中的一個(gè)OS_MEM資源,OS_MEM結(jié)構(gòu)體定義如下:
typedef struct { /* MEMORY CONTROL BLOCK */void *OSMemAddr; /* Pointer to beginning of memory partition */void *OSMemFreeList; /* Pointer to list of free memory blocks */INT32U OSMemBlkSize; /* Size (in bytes) of each block of memory */INT32U OSMemNBlks; /* Total number of blocks in this partition */INT32U OSMemNFree; /* Number of memory blocks remaining in this partition */ } OS_MEM;2. MEM的接口函數(shù)
? ? ?2. 主要接口函數(shù)諸如獲取一個(gè)內(nèi)存塊或者釋放一個(gè)內(nèi)存塊,都是對(duì)上述鏈表的操作。但由于鏈表的定義,鏈表的插入和刪除變得很簡(jiǎn)單。例如返回一個(gè)內(nèi)存塊給內(nèi)存分區(qū):
*(void **)pblk = pmem->OSMemFreeList; //把當(dāng)前的pFree作為pBlk的值 pmem->OSMemFreeList = pblk;//pblk賦值給pFreepmem->OSMemNFree++;返回前:pData -> OSMemFreeList(old),
返回后: pData -> pblk -> OSMemFreeList(Old); pblk成為當(dāng)前的OSMemFreeList(New)
獲取一個(gè)內(nèi)存塊給內(nèi)存分區(qū):
pblk = pmem->OSMemFreeList; //當(dāng)前空節(jié)點(diǎn)的地址返回 pmem->OSMemFreeList = *(void **)pblk; //當(dāng)前空節(jié)點(diǎn)的值(即pNext)賦值給OSMemFreeList pmem->OSMemNFree--;其他:OS_MEM_DATA 定義了一個(gè)內(nèi)存塊的信息,在OSMemQuery中使用,沒(méi)有特別的意義。
總結(jié)
- 上一篇: 有关推挽输出、开漏输出、复用开漏输出、复
- 下一篇: 启动ucosii之OSInit()