1、動態內存管理算法
動態內存管理是一個真實的堆(Heap)內存管理模塊,能夠在當前資源知足的狀況下,根據用戶的需求分配任意大小的內存塊。而當用戶不須要再使用這些內存塊時,又能夠釋放回堆中供其餘應用分配使用。RT-Thread系統爲了知足不一樣的需求,提供了兩套不一樣的動態內存管理算法,分別是小內存管理算法和SLAB內存管理算法。小堆內存管理模塊主要針對系統資源比較少,通常用於小於2M內存空間的系統;而SLAB內存管理模塊則主要是在系統資源比較豐富時,提供了一種近似多內存池管理算法的快速算法。安全
兩種內存管理模塊在系統運行時只能選擇其中之一或者徹底不使用動態堆內存管理器。這兩種管理模塊提供的API接口徹底相同。由於動態內存管理器要知足多線程狀況下的安全分配,會考慮多線程間的互斥問題,因此請不要在中斷服務例程中分配或釋放動態內存塊。由於它可能會引發當前上下文被掛起等待。數據結構
2、小內存管理算法多線程
本文主要介紹小內存管理算法,至於SLAB內存管理算法則在後續文章中介紹。小內存管理算法是一個簡單的內存分配算法。初始時,它是一塊大的內存。當須要分配內存塊時,將從這個大的內存塊上分割出相匹配的內存塊,而後把分割出來的空閒內存塊還回給堆管理系統中。每一個內存塊都包含一個管理用的數據頭,經過這個頭把使用塊與空閒塊用雙向鏈表的方式連接起來,如 內存塊鏈表圖所示:函數
內存管理的在表現主要體如今內存的分配與釋放上,小型內存管理算法能夠用如下例子體現出來。ui
空閒鏈表指針lfree初始指向32字節的內存塊。當用戶線程要再分配一個64字節的內存塊時,但此lfree指針指向的內存塊只有32字節並不能知足要求,內存管理器會繼續尋找下一內存塊,當找到再下一塊內存塊,128字節時,它知足分配的要求。由於這個內存塊比較大,分配器將把此內存塊進行拆分,餘下的內存塊(52字節)繼續留在lfree鏈表中。另外,在每次分配內存塊前,都會留出12字節數據頭用於magic,used信息及鏈表節點使用。返回給應用的地址其實是這塊內存塊12字節之後的地址,前面的12字節數據頭是用戶永遠不該該碰的部分。(注:12字節數據頭長度會與系統對齊差別而有所不一樣)。釋放時則是相反的過程,但分配器會查看先後相鄰的內存塊是否空閒,若是空閒則合併成一個大的空閒內存塊。spa
數據結構:在src/mem.c中線程
#define HEAP_MAGIC 0x1ea0 struct heap_mem { /* magic and used flag */ rt_uint16_t magic; //若是此內存塊被分配了,則(在rt_malloc中設置)置0x1ea0。標誌該內存塊爲一個內存管理系統使用的動態內存數據塊,相似於一個內存保護字:若是這個區域被改寫,那麼也就意味着這塊內存塊被非法改寫 (正常狀況下只有內存管理系統纔會訪問它) rt_uint16_t used; //0:未分配;1:已分配 rt_size_t next, prev; //後一內存塊首地址(包含內存塊控制結構),前一內存塊首地址(包含內存塊控制結構)。注意這裏再也不是rt_list_t類型(鏈表類型),它們直接賦值爲內存地址 };
3、小內存管理算法函數接口:在src/mem.c中指針
初始化動態內存堆: void rt_system_heap_init(void *begin_addr, void *end_addr); 在使用堆內存RT_USING_HEAP時,必需要在系統初始化的時候進行堆內存的初始化。這個函數會把參數begin_addr,end_addr區域的內存空間做爲內存堆來使用。 由源代碼可知,初始化時小內存管理算法經過傳進來的起始地址和末尾地址將動態堆內存初始化爲兩個內存塊:第一個內存塊指向動態堆內存首地址,可用空間爲整個可分配的內存(不包含兩個內存控制塊自己所佔大小,即減去24字節),此內存塊下一指針指向末尾內存控制塊;第二個內存塊指向最末尾的一個內存控制塊,可用空間大小爲0,此內存塊前一指針和後一指針都指向自己。 分配內存塊: void *rt_malloc(rt_size_t size); rt_malloc函數會從系統堆空間中找到合適大小的內存塊,而後把內存塊可用地址返回給用戶。 重分配內存塊: void *rt_realloc(void *rmem, rt_size_t newsize); 在已分配內存塊的基礎上從新分配內存塊的大小(增長或縮小),在進行從新分配內存塊時,原來的內存塊數據保持不變(縮小的狀況下,後面的數據被自動截斷)。
由代碼可知,若是當前內存塊可用內存比較充裕時,將分割成兩塊,後一塊分割出來後會嘗試與先後內存塊合併。 分配多塊內存: void *rt_calloc(rt_size_t count, rt_size_t size); 從內存堆中分配連續內存地址的多個內存塊,返回的指針指向第一個內存塊的地址,而且全部分配的內存塊都被初始化成零。 釋放內存: void rt_free(void *rmem); 用戶線程使用完從內存分配器中申請的內存後,必須及時釋放,不然會形成內存泄漏,rt_free函數會把待釋放的內存換回給堆管理器中。在調用這個函數時用戶需傳遞待釋放的內存塊指針,若是是空指針直接返回。
內存合併: static void plug_holes(struct heap_mem *mem); 此函數在重分配內存時調用,將分割出來的後一部分嘗試與先後內存塊合併;當釋放內存時,算法將檢查待釋放內存的前一內存塊和後一內存塊,若是爲空閒則合併。
4、算法總結code
小內存管理算法從總體上來說,是將一片內存初始化爲靜態鏈表來實現的,初始化時只有兩個內存塊:
第一塊除了包含內存塊控制塊(佔用12字節)外,還包含待分配的空間,這個空間就是mem_size_aligned,它是指此算法可用來做分配的動態堆內存總大小,任何待分配的內存都不能比它還大,不然超過極限;
第二塊只包含內存控制塊自己(佔用12字節),不包含待分配的空間,它做爲鏈表尾,且設置使用標誌used爲1(永久使用)。
整個算法中還有一空閒指針lfree,始終指向動態內存堆中剩餘可用空間的第一個空閒內存塊,初始化時指向動態內存堆起始地址,該指針變量在分配內存和合並內存時會不斷更新。接下來就是分配內存了,分配內存時首先從空閒內存所指向的節點開始掃描,一旦掃描到大小知足的節點,則返回此節點,若此節點所指向的空間足夠大,大到還有足夠空間分配另外一內存塊(只含內存塊控制結構)時,則分割此節點指向的內存爲兩塊,前一塊內存可用空間user data首地址返回,後一塊內存設置爲未分配(空閒內存); 當釋放內存時,算法將檢查待釋放內存的前一內存塊和後一內存塊,若是爲空閒則合併。