CosOS核心中使用一個核心堆來管理記憶體,核心通過kmalloc和kfree從核心堆中申請和釋放記憶體。CosOS為使用者態編寫的庫函數中也實現了使用者態堆,應用程式通過malloc和free從堆中申請釋放記憶體。 核心堆和使用者態堆的演算法類似,都通過調用alloc和free_int來操作堆。下面以代碼和注釋的形式詳細介紹CosOS中 alloc和free_int這兩個函數的實現。在堆中,可以用的空閑記憶體塊成為hole,堆使用了一個鏈表來儲存堆中所有可用的hole。Alloc函數就是在這個鏈表中尋找到一個大小合適的hole然後根據請求分配的大小把hole切分成兩部分,一部分返回給調用者使用,剩下的重新加入到鏈表中。/* alloc兩個參數,size代表請求分配記憶體的大小,page_align代表請求記憶體空間的首地址是否必須頁對齊,即地址是能被0x1000整除的。實現這個特性是因為核心中有些空間必須頁對齊,例如頁表、也目錄。*/void *alloc(u32int size, u8int page_align){ register struct hole *hp, *prev_ptr; u32int old_base; prev_ptr = NIL_HOLE; hp = hole_head;//通過變數hole鏈表,尋找一個合適的holewhile(hp != NIL_HOLE) { if(page_align) {//由於頁數限制,頁對齊實現部分代碼省略。 }else if (hp->h_len >= size) {//找到了一個足夠大的hole,使用這個hole。//將其地址儲存在臨時變數old_base中,作為alloc的傳回值, old_base =hp->h_base; //由於hole比請求的空間大,剩餘部分需放入堆中。只需重新調整hole的首地址和長度即可 hp->h_base +=size; hp->h_len -=size;//記住使用使用過的記憶體當中,最高的地址,儲存在high_watermark當中 if(hp->h_base > high_watermark) high_watermark= hp->h_base;//經過上面代碼調整hole的大小之後,如果hole的大小為0,即hole的大小與請求大小相同,則將其從hole鏈表中移除。 if (hp->h_len == 0) del_slot(prev_ptr,hp);//返回分配記憶體塊得首地址。 return (void*)(old_base); } prev_ptr = hp; hp = hp->h_next; }//若運行到這裡,說明堆中沒有合適的記憶體塊,返回一個null 指標。 return0;}free_int用於將首地址為base,長度為clicks的記憶體塊釋放,重新加入到堆的hole列表中。void free_int(u32int base, u32int clicks){ struct hole*hp, *new_ptr, *prev_ptr;//如果要釋放的記憶體塊大小為0,則直接返回 if (clicks ==0) return; if ( (new_ptr= free_slots) == NIL_HOLE) ASSERT(0);//將記憶體塊設定成一個holenew_ptr->h_base = base; new_ptr->h_len = clicks; free_slots = new_ptr->h_next; hp = hole_head;//如果記憶體塊得地址是當前hole鏈表中最小的或者hole鏈表為空白,則把它插入到鏈表的第一個位置。 if (hp ==NIL_HOLE || base <= hp->h_base) {//插入到第一個位置 new_ptr->h_next = hp; hole_head = new_ptr;//檢測是否能與相鄰的hole合并成一個更大的hole,這樣可以減少記憶體片段。merge(new_ptr); return; }//hole沒有被加入到首位置,則執行下面代碼 prev_ptr = NIL_HOLE;//hole鏈表中的hole是按照記憶體塊首地址由低到高排序的,插入新的hole時必須保證插入之後鏈表還是有序的。因此需要找到一個合適的位置插入。 while (hp !=NIL_HOLE && base > hp->h_base) { prev_ptr = hp; hp = hp->h_next; }//找到了合適的位置,插入其中 new_ptr->h_next = prev_ptr->h_next; prev_ptr->h_next = new_ptr;//檢測是否能與相鄰的hole合并成一個更大的hole,這樣可以減少記憶體片段。 merge(prev_ptr); }Hole合并的實現較為簡單,只需檢測hole的base加上size是否與下一hole的base相同即可。這裡不詳細介紹alloc和free_int是最核心最原始的兩個函數。free_int使用時第二個參數需要確定釋放記憶體的大小,而使用者態的malloc、free中,free是不需要知道記憶體大小,只需傳入需要釋放的地址即可。為了在使用free時無需知道記憶體塊得大小也可以正確釋放記憶體塊,CosOS中採用的方法是,將記憶體塊得大小儲存在記憶體塊首地址前得一個整型空間中,具體實現如下://size為申請記憶體的大小void * malloc(u32int size){//使用alloc從堆中申請記憶體時,多申請4個位元組,用來儲存記憶體塊大小。 void *p= alloc(size + 4);//在返回記憶體塊的前4個位元組中(即一個整型中)儲存記憶體塊大小。 *((u32int*)p) = size;//返回這4個位元組之後的地址,防止使用記憶體塊時,大小被破壞。 return(void*)((u32int)p + 4);}void free(void *p){//從記憶體塊首地址的前4個位元組中擷取記憶體塊的大小,儲存在size中。 u32int size = *((u32int*)p - 1);//使用free_int釋放這塊記憶體。 free_int((u32int)p - 4, size + 4);}