💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
内存释放的操作也是比较简单的,LwIP是这样子做的:它根据用户释放的内存块地址,通过偏移mem结构体大小得到正确的内存块起始地址,并且根据mem中保存的内存块信息进行释放、合并等操作,并将used字段清零,表示该内存块未被使用。 LwIP为了防止内存碎片的出现,通过算法将内存相邻的两个空闲内存块进行合并,在释放内存块的时候,如果内存块与上一个或者下一个空闲内存块在地址上是连续的,那么就将这两个内存块进行合并,其源码具体见代码清单 5-12。 ``` 1 2 void 3 mem_free(void *rmem) 4 { 5 struct mem *mem; 6 LWIP_MEM_FREE_DECL_PROTECT(); 7 8 if (rmem == NULL) (1) 9 { 10 LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_TRACE | 11 LWIP_DBG_LEVEL_SERIOUS, 12 ("mem_free(p == NULL) was called.\n")); 13 return; 14 } 15 if ((((mem_ptr_t)rmem) & (MEM_ALIGNMENT - 1)) != 0) 16 { 17 LWIP_MEM_ILLEGAL_FREE("mem_free: sanity check alignment"); 18 LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, 19 ("mem_free: sanity check alignment\n")); 20 /* protect mem stats from concurrent access */ 21 MEM_STATS_INC_LOCKED(illegal); 22 return; 23 } 24 25 mem = (struct mem *)(void *)((u8_t *)rmem - 26 (SIZEOF_STRUCT_MEM + MEM_SANITY_OFFSET)); (2) 27 28 if ((u8_t *)mem < ram || 29 (u8_t *)rmem + MIN_SIZE_ALIGNED > (u8_t *)ram_end) (3) 30 { 31 LWIP_MEM_ILLEGAL_FREE("mem_free: illegal memory"); 32 LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, 33 ("mem_free: illegal memory\n")); 34 /* protect mem stats from concurrent access */ 35 MEM_STATS_INC_LOCKED(illegal); 36 return; 37 } 38 39 /* protect the heap from concurrent access */ 40 LWIP_MEM_FREE_PROTECT(); 41 42 /* mem has to be in a used state */ 43 if (!mem->used) (4) 44 { 45 LWIP_MEM_ILLEGAL_FREE("mem_free: illegal \ 46 memory: double free"); 47 LWIP_MEM_FREE_UNPROTECT(); 48 LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, 49 ("mem_free: illegal memory: double free?\n")); 50 /* protect mem stats from concurrent access */ 51 MEM_STATS_INC_LOCKED(illegal); 52 return; 53 } 54 55 if (!mem_link_valid(mem)) (5) 56 { 57 LWIP_MEM_ILLEGAL_FREE("mem_free: illegal memory:\ 58 non-linked: double free"); 59 LWIP_MEM_FREE_UNPROTECT(); 60 LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, 61 ("mem_free: illegal memory: non-linked: double free?\n")); 62 /* protect mem stats from concurrent access */ 63 MEM_STATS_INC_LOCKED(illegal); 64 return; 65 } 66 67 /* mem is now unused. */ 68 mem->used = 0; (6) 69 70 if (mem < lfree) 71 { 72 /* the newly freed struct is now the lowest */ 73 lfree = mem; (7) 74 } 75 76 MEM_STATS_DEC_USED(used, mem->next - 77 (mem_size_t)(((u8_t *)mem - ram))); 78 79 /* finally, see if prev or next are free also */ 80 plug_holes(mem); (8) 81 82 MEM_SANITY(); 83 84 LWIP_MEM_FREE_UNPROTECT(); 85 } ``` * (1):如果释放的地址为空,则直接返回。 * (2):对释放的地址进行偏移,得到真正内存块的起始地址。 * (3):判断一下内存块的起始地址是否合法,如果不合法则直接返回。 * (4):判断一下内存块是否被使用,如果是未使用的也直接返回。 * (5):判断一下内存块在链表中的连接是否正常,如果不正常也直接返回。 * (6):程序执行到这里,表示内存块能正常释放,就将used置0表示已经释放了内存块。 * (7):如果刚刚释放的内存块地址比lfree指向的内存块地址低,则更新lfree指针。 * (8):调用plug_holes()函数尝试进行内存块合并,如果能合并则合并,该函数就是我们说的内存块合并算法,只要新释放的内存块与上一个或者下一个空闲内存块在地址上是连续的,则进行合并,该函数的代码具体见代码清单 5-13,该函数比较容易理解,就不做过多赘述。 ``` 1 static void 2 plug_holes(struct mem *mem) 3 { 4 struct mem *nmem; 5 struct mem *pmem; 6 7 LWIP_ASSERT("plug_holes: mem >= ram", (u8_t *)mem >= ram); 8 LWIP_ASSERT("plug_holes: mem < ram_end", 9 (u8_t *)mem < (u8_t *)ram_end); 10 LWIP_ASSERT("plug_holes: mem->used == 0", mem->used == 0); 11 12 /* plug hole forward */ 13 LWIP_ASSERT("plug_holes: mem->next <= MEM_SIZE_ALIGNED", 14 mem->next <= MEM_SIZE_ALIGNED); 15 16 nmem = ptr_to_mem(mem->next); 17 if (mem != nmem && nmem->used == 0 && 18 (u8_t *)nmem != (u8_t *)ram_end) 19 { 20 /* if mem->next is unused and not end of ram, combine mem and mem->next */ 21 if (lfree == nmem) 22 { 23 lfree = mem; 24 } 25 mem->next = nmem->next; 26 if (nmem->next != MEM_SIZE_ALIGNED) 27 { 28 ptr_to_mem(nmem->next)->prev = mem_to_ptr(mem); 29 } 30 } 31 32 /* plug hole backward */ 33 pmem = ptr_to_mem(mem->prev); 34 if (pmem != mem && pmem->used == 0) 35 { 36 /* if mem->prev is unused, combine mem and mem->prev */ 37 if (lfree == mem) 38 { 39 lfree = pmem; 40 } 41 pmem->next = mem->next; 42 if (mem->next != MEM_SIZE_ALIGNED) 43 { 44 ptr_to_mem(mem->next)->prev = mem_to_ptr(pmem); 45 } 46 } 47 } ``` 对内存释放函数的操作要非常小心,尤其是传递给函数的参数,该参数必须是内存申请返回的地址,这样子才能保证系统根据该地址去寻找内存块中的mem结构体,最终通过操作mem结构体才能实现内存块的释放操作,并且这样子才有可能进行内存块的合并,否则就没法正常合并内存块,还会把整个内存堆打乱,这样子就会很容易产生内存碎片。 此外,用户在申请内存的时候要注意及时释放内存块,否则就会造成内存泄漏,什么是内存泄漏呢?就是用户在调用内存分配函数后,没有及时或者进行错误的内存释放操作,一次两次这样子的操作并没有什么影响,如果用户周期性调用mem_malloc()函数进行内存申请,并且在内存使用完的时候么有释放,这样子程序就会用完内存堆中的所有内存,最终导致内存耗尽无法申请内存,出现死机等现象。