本文參考書:操作系統真像還原 什麼是malloc? malloc 是用戶態申請記憶體時使用的函數。 malloc在哪裡申請? 堆中。 什麼是堆? 程式運行過程中需要申請額外的記憶體都會在堆中分配,堆中的記憶體分為幾個規格類型的塊用鏈表保存,程式需要記憶體就分配一個大於等於所需記憶體大小的塊。如果一個規格的塊用 ...
本文參考書:操作系統真像還原
什麼是malloc?
malloc 是用戶態申請記憶體時使用的函數。
malloc在哪裡申請?
堆中。
什麼是堆?
程式運行過程中需要申請額外的記憶體都會在堆中分配,堆中的記憶體分為幾個規格類型的塊用鏈表保存,程式需要記憶體就分配一個大於等於所需記憶體大小的塊。如果一個規格的塊用完了就像系統申請頁,再將頁切分成規格塊的大小一個一個用鏈錶鏈接起來。
如何找到堆?
一般堆在進程pcb處有指針,內核的堆可以是一個全局變數。
由以上幾個問題我們可以知道,只要先搞定堆後,malloc的作用就是在堆中拿個記憶體塊就好啦。
堆的結構圖
堆的核心結構是個數組u_block_desc 代表用戶堆,放在進程pcb中。數組的每個元素mem_block_desc 代表不同類型的記憶體塊大小,每個記憶體塊用鏈錶鏈接。
幾個重要數據結構,下麵會進行解釋
/* 記憶體塊 */ struct mem_block { struct list_elem free_elem; }; /* 記憶體塊描述符 */ struct mem_block_desc { uint32_t block_size; // 記憶體塊大小 uint32_t blocks_per_arena; // 本arena中可容納此mem_block的數量. struct list free_list; // 目前可用的mem_block鏈表 };
/* 記憶體倉庫arena元信息 */
struct arena {
struct mem_block_desc* desc; // 此arena關聯的mem_block_desc
/* large為ture時,cnt表示的是頁框數。
* 否則cnt表示空閑mem_block數量 */
uint32_t cnt;
bool large;
};
mem_block_desc 就是記憶體塊的信息,記錄著記憶體塊大小和鏈表
arena就是分配的頁,arena中記憶體塊切割大小由desc指定,如果所需記憶體塊過大,就不進行切割直接分配頁框,也就是large的作用。
記憶體分配過程如下
① 向堆申請可用記憶體時,沒有在所要用到的mem_block_desc下麵找到可用記憶體塊。向操作系統申請4K的頁,每個頁就是一個arena。
②對arena進行切分,分為所需mem_block_desc的大小,並且一個一個加入到mem_block_desc的鏈表中
③從鏈表上拿到一個記憶體塊並返回記憶體地址
1 void* sys_malloc(uint32_t size) { 2 enum pool_flags PF; 3 struct pool* mem_pool; 4 uint32_t pool_size; 5 struct mem_block_desc* descs; 6 struct task_struct* cur_thread = running_thread(); 7 8 /* 判斷用哪個記憶體池,內核也要從內核記憶體池申請頁*/ 9 if (cur_thread->pgdir == NULL) { // 若為內核線程 10 PF = PF_KERNEL; 11 pool_size = kernel_pool.pool_size; 12 mem_pool = &kernel_pool; 13 descs = k_block_descs; 14 } else { // 用戶進程pcb中的pgdir會在為其分配頁表時創建 15 PF = PF_USER; 16 pool_size = user_pool.pool_size; 17 mem_pool = &user_pool; 18 descs = cur_thread->u_block_desc; 19 } 20 21 /* 若申請的記憶體不在記憶體池容量範圍內則直接返回NULL */ 22 if (!(size > 0 && size < pool_size)) { 23 return NULL; 24 } 25 struct arena* a; 26 struct mem_block* b; 27 lock_acquire(&mem_pool->lock); 28 29 /* 超過最大記憶體塊1024, 就分配頁框 */ 30 if (size > 1024) { 31 uint32_t page_cnt = DIV_ROUND_UP(size + sizeof(struct arena), PG_SIZE); // 向上取整需要的頁框數 32 33 a = malloc_page(PF, page_cnt); 34 35 if (a != NULL) { 36 memset(a, 0, page_cnt * PG_SIZE); // 將分配的記憶體清0 37 38 /* 對於分配的大塊頁框,將desc置為NULL, cnt置為頁框數,large置為true */ 39 a->desc = NULL; 40 a->cnt = page_cnt; 41 a->large = true; 42 lock_release(&mem_pool->lock); 43 return (void*)(a + 1); // 跨過arena大小,把剩下的記憶體返回 44 } else { 45 lock_release(&mem_pool->lock); 46 return NULL; 47 } 48 } else { // 若申請的記憶體小於等於1024,可在各種規格的mem_block_desc中去適配 49 uint8_t desc_idx; 50 51 /* 從記憶體塊描述符中匹配合適的記憶體塊規格 */ 52 for (desc_idx = 0; desc_idx < DESC_CNT; desc_idx++) { 53 if (size <= descs[desc_idx].block_size) { // 從小往大後,找到後退出 54 break; 55 } 56 } 57 58 /* 若mem_block_desc的free_list中已經沒有可用的mem_block, 59 * 就創建新的arena提供mem_block */ 60 if (list_empty(&descs[desc_idx].free_list)) { 61 a = malloc_page(PF, 1); // 分配1頁框做為arena 62 if (a == NULL) { 63 lock_release(&mem_pool->lock); 64 return NULL; 65 } 66 memset(a, 0, PG_SIZE); //清空頁數據 67 68 /* 對於分配的小塊記憶體,將desc置為相應記憶體塊描述符, 69 * cnt置為此arena可用的記憶體塊數,large置為false */ 70 a->desc = &descs[desc_idx]; 71 a->large = false; 72 a->cnt = descs[desc_idx].blocks_per_arena; 73 uint32_t block_idx; 74 75 enum intr_status old_status = intr_disable(); 76 77 /* 開始將arena拆分成記憶體塊,並添加到記憶體塊描述符的free_list中 */ 78 for (block_idx = 0; block_idx < descs[desc_idx].blocks_per_arena; block_idx++) { 79 b = arena2block(a, block_idx); 80 ASSERT(!elem_find(&a->desc->free_list, &b->free_elem)); 81 list_append(&a->desc->free_list, &b->free_elem); 82 } 83 intr_set_status(old_status); 84 } 85 86 /* 開始分配記憶體塊 */ 87 b = elem2entry(struct mem_block, free_elem, list_pop(&(descs[desc_idx].free_list))); 88 memset(b, 0, descs[desc_idx].block_size); 89 90 a = block2arena(b); // 獲取記憶體塊b所在的arena 91 a->cnt--; // 將此arena中的空閑記憶體塊數減1 92 lock_release(&mem_pool->lock); 93 return (void*)b; 94 } 95 }