rt-thread的小記憶體管理是其預設的堆記憶體管理演算法。是採用靜態鏈表來實現的,源檔案為mem.c。1.資料結構===== struct heap_mem { /* magic and used flag */ rt_uint16_t magic; // 如果此記憶體塊被分配了,則置0x1ea0,以此標誌 // 此塊記憶體是正常分配出來的,而不是非法指標 rt_uint16_t used; // 0:未分配;1:已指派 rt_size_t next, prev; // 前一記憶體塊,後一記憶體塊 }; 2.初始化動態記憶體堆===== /** * @ingroup SystemInit * * This function will init system heap * * @param begin_addr the beginning address of system page * @param end_addr the end address of system page */ void rt_system_heap_init(void *begin_addr, void *end_addr) { struct heap_mem *mem; rt_uint32_t begin_align = RT_ALIGN((rt_uint32_t)begin_addr, RT_ALIGN_SIZE); // 得到對齊後的堆記憶體起始地址 // 在rtdef.h中 // #define RT_ALIGN(size, align) (((size) + (align) - 1) & ~((align) - 1)) // 使得begin_align保持四字對齊,這樣對於處理器定址時比較高效 rt_uint32_t end_align = RT_ALIGN_DOWN((rt_uint32_t)end_addr, RT_ALIGN_SIZE); // 得到對齊後的堆記憶體末尾地址 // #define RT_ALIGN_DOWN(size, align) ((size) & ~((align) - 1)) // 使得末尾地址向後四字對齊 RT_DEBUG_NOT_IN_INTERRUPT; // 確保此函數不是運行在中斷常式內 // 在rtdebug.h中 // #define RT_DEBUG_NOT_IN_INTERRUPT \ // do \ // { \ // rt_base_t level; \ // level = rt_hw_interrupt_disable(); \ // if (rt_interrupt_get_nest() != 0) \ // { \ // rt_kprintf("Function[%s] shall not used in ISR\n", __FUNCTION__); \ // RT_ASSERT(0) \ // } \ // rt_hw_interrupt_enable(level); \ // } \ // while (0) // 可以判斷出是否處於中斷嵌套中,保證初始化記憶體堆不在中斷中執行 /* alignment addr */ if ((end_align > (2 * SIZEOF_STRUCT_MEM)) && ((end_align - 2 * SIZEOF_STRUCT_MEM) >= begin_align)) // 確保可用動態堆記憶體大小至少大於可等於2個記憶體塊控制結構大小 // 在初始化中,分配兩個記憶體控制塊,一個指向開始,一個指向末尾 { /* calculate the aligned memory size */ mem_size_aligned = end_align - begin_align - 2 * SIZEOF_STRUCT_MEM; // 計算出還可以分配的記憶體大小,要減去兩個記憶體控制塊所佔的空間 } else { rt_kprintf("mem init, error begin address 0x%x, and end address 0x%x\n", (rt_uint32_t)begin_addr, (rt_uint32_t)end_addr); return; } /* point to begin address of heap */ heap_ptr = (rt_uint8_t *)begin_align; // heap_ptr為靜態全域變數,指用堆記憶體起始地址 RT_DEBUG_LOG(RT_DEBUG_MEM, ("mem init, heap begin address 0x%x, size %d\n", (rt_uint32_t)heap_ptr, mem_size_aligned)); /* initialize the start of the heap */ mem = (struct heap_mem *)heap_ptr; // 將堆的起始地址初始化為一個記憶體塊 mem->magic = HEAP_MAGIC; // 初始化為0x1ea0,用來標示 mem->next = mem_size_aligned + SIZEOF_STRUCT_MEM; // 鏈表的下一個為末尾地址,需要加一個記憶體塊指塊的大小,即 // 可用大小為整個空間。且這裡是用的相對位置 mem->prev = 0; // 無前一個記憶體塊 mem->used = 0; // 初始化為未使用 /* initialize the end of the heap */ heap_end = (struct heap_mem *)&heap_ptr[mem->next]; // 指向末尾記憶體塊,將末尾地址初始化為一個記憶體塊 heap_end->magic = HEAP_MAGIC; heap_end->used = 1; // 末尾記憶體塊初始化為已使用 heap_end->next = mem_size_aligned + SIZEOF_STRUCT_MEM; // 下一個記憶體塊指向自己 heap_end->prev = mem_size_aligned + SIZEOF_STRUCT_MEM; // 前一個記憶體塊也指向自己 rt_sem_init(&heap_sem, "heap", 1, RT_IPC_FLAG_FIFO); // 初始化堆記憶體訊號量為1 /* initialize the lowest-free pointer to the start of the heap */ lfree = (struct heap_mem *)heap_ptr; // lfree始終指向最小位置的空閑記憶體塊,這裡指向初始地址 } 3.分配記憶體=====/** * Allocate a block of memory with a minimum of 'size' bytes. * * @param size is the minimum size of the requested block in bytes. * * @return pointer to allocated memory or NULL if no free memory was found. */void *rt_malloc(rt_size_t size){ rt_size_t ptr, ptr2; struct heap_mem *mem, *mem2; RT_DEBUG_NOT_IN_INTERRUPT; if (size == 0) return RT_NULL; if (size != RT_ALIGN(size, RT_ALIGN_SIZE)) RT_DEBUG_LOG(RT_DEBUG_MEM, ("malloc size %d, but align to %d\n", size, RT_ALIGN(size, RT_ALIGN_SIZE))); else RT_DEBUG_LOG(RT_DEBUG_MEM, ("malloc size %d\n", size)); /* alignment size */ size = RT_ALIGN(size, RT_ALIGN_SIZE); // 首先將地址對齊 if (size > mem_size_aligned) // 如果地址大於可用地址,則說明記憶體不夠用了 { RT_DEBUG_LOG(RT_DEBUG_MEM, ("no memory\n")); return RT_NULL; } /* every data block must be at least MIN_SIZE_ALIGNED long */ if (size < MIN_SIZE_ALIGNED) size = MIN_SIZE_ALIGNED; // 在mem.c中 // #define MIN_SIZE 12 // #define MIN_SIZE_ALIGNED RT_ALIGN(MIN_SIZE, RT_ALIGN_SIZE) // 分配的記憶體必須要大於最小要求 /* take memory semaphore */ rt_sem_take(&heap_sem, RT_WAITING_FOREVER); // 擷取訊號量,和其他記憶體配置函數保持同步 for (ptr = (rt_uint8_t *)lfree - heap_ptr; ptr < mem_size_aligned - size; ptr = ((struct heap_mem *)&heap_ptr[ptr])->next) // 從最小位置記憶體塊開始尋找閒置記憶體塊。ptr指的是相對位置 { mem = (struct heap_mem *)&heap_ptr[ptr]; // 指向遍曆得到的記憶體塊,然後進行判斷是否可用和大小是否滿足 if ((!mem->used) && (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) { /* mem is not used and at least perfect fit is possible: * mem->next - (ptr + SIZEOF_STRUCT_MEM) gives us the 'user data size' of mem */ // 第一句判斷是否是未使用的記憶體塊 // 然後判斷除去記憶體控制塊之後的記憶體空間大小是否滿足要求 if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED)) { /* (in addition to the above, we test if another struct heap_mem (SIZEOF_STRUCT_MEM) containing * at least MIN_SIZE_ALIGNED of data also fits in the 'user data space' of 'mem') * -> split large block, create empty remainder, * remainder must be large enough to contain MIN_SIZE_ALIGNED data: if * mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) == size, * struct heap_mem would fit in but no data between mem2 and mem2->next * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty * region that couldn't hold data, but when mem->next gets freed, * the 2 regions would be combined, resulting in more free memory */ // 這裡,當這個記憶體塊夠分配所需大小,且剩下的空間還能分配一個滿足最小空間的記憶體塊 ptr2 = ptr + SIZEOF_STRUCT_MEM + size; // 這段記憶體被分成兩個塊,ptr2指向後一個空閑塊 /* create mem2 struct */ // mem2指向了後一個記憶體塊 mem2 = (struct heap_mem *)&heap_ptr[ptr2]; mem2->used = 0; mem2->next = mem->next; mem2->prev = ptr; // 這裡實現的是一個鏈表的插入。m /* and insert it between mem and mem->next */ // 插入完成之後,mem為新分配的那個記憶體塊 // mem2為新的空閑記憶體塊 mem->next = ptr2; mem->used = 1; if (mem2->next != mem_size_aligned + SIZEOF_STRUCT_MEM) // 如果後一個記憶體塊mem2不是末尾,則讓其mem2的prev指向這個空閑記憶體塊 { ((struct heap_mem *)&heap_ptr[mem2->next])->prev = ptr2; }#ifdef RT_MEM_STATS used_mem += (size + SIZEOF_STRUCT_MEM); if (max_mem < used_mem) max_mem = used_mem;#endif } else { // 閒置只夠本次分配 /* (a mem2 struct does no fit into the user data space of mem and mem->next will always * be used at this point: if not we have 2 unused structs in a row, plug_holes should have * take care of this). * -> near fit or excact fit: do not split, no mem2 creation * also can't move mem->next directly behind mem, since mem->next * will always be used at this point! */ mem->used = 1;#ifdef RT_MEM_STATS used_mem += mem->next - ((rt_uint8_t*)mem - heap_ptr); if (max_mem < used_mem) max_mem = used_mem;#endif } /* set memory block magic */ mem->magic = HEAP_MAGIC; if (mem == lfree) // 如果更好是一個最小位置空閑記憶體塊 { /* Find next free block after mem and update lowest free pointer */ // 尋找下一個空閑記憶體塊,並更新lfree的值,使其指向最小位置記憶體塊 while (lfree->used && lfree != heap_end) lfree = (struct heap_mem *)&heap_ptr[lfree->next]; RT_ASSERT(((lfree == heap_end) || (!lfree->used))); } rt_sem_release(&heap_sem); RT_ASSERT((rt_uint32_t)mem + SIZEOF_STRUCT_MEM + size <= (rt_uint32_t)heap_end); RT_ASSERT((rt_uint32_t)((rt_uint8_t *)mem + SIZEOF_STRUCT_MEM) % RT_ALIGN_SIZE == 0); RT_ASSERT((((rt_uint32_t)mem) & (RT_ALIGN_SIZE-1)) == 0); RT_DEBUG_LOG(RT_DEBUG_MEM, ("allocate memory at 0x%x, size: %d\n", (rt_uint32_t)((rt_uint8_t *)mem + SIZEOF_STRUCT_MEM), (rt_uint32_t)(mem->next - ((rt_uint8_t *)mem - heap_ptr)))); RT_OBJECT_HOOK_CALL(rt_malloc_hook, (((void *)((rt_uint8_t *)mem + SIZEOF_STRUCT_MEM)), size)); /* return the memory data except mem struct */ return (rt_uint8_t *)mem + SIZEOF_STRUCT_MEM; // 返回記憶體塊地址,不包括記憶體控制塊 } } rt_sem_release(&heap_sem); // 都不符合,返回RT_NULL return RT_NULL;}