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