用户可以向系统申请分配任意大小内存块(有大小限制,最小不能小于MIN_SIZE) 下面是与内存堆管理相关数据结构,内存堆的管理类似于双向链表
名称类型所在文件描述ram_heap[]全局型数组mem.c系统堆内存空间ram全局型指针mem.c指向内存堆对齐后起始地址mem结构体mem.c附加在每个内存块前面的结构体ram_endmem型指针mem.c指向系统最后一个内存块lfreemem型指针mem.c指向当前系统具有最低地址的空闲内存块mem_mutex互斥量mem.c用于内存堆的互斥信号量mem.h是声明了一些函数、定义了一些宏定义,这些宏定义是做些字节对齐计算在内存池实现中也有使用到,如下 mem.c主要定义了mem结构体,内存堆函数实现,内存堆数据定义
这里解释下 next 下一个内存块相对于内存堆首地址偏移量 prev 上一个内存块相对于内存堆首地址偏移量 used 1表示mem结构体管理的内存块已分配,0表示未被分配
用户定义的可用内存堆大小是MEM_SIZE,在lwipopt.h和opt.h中宏定义,但是实际定义的堆空间还要加上2个struct mem结构体大小,上述空间均要字节对齐,最后定义出ram_heap
1. mem_init 初始化完成后如下图
2. mem_malloc
void * mem_malloc(mem_size_t size) { mem_size_t ptr, ptr2; struct mem *mem, *mem2; if (size == 0) { return NULL; } //对要分配size做字节对齐 size = LWIP_MEM_ALIGN_SIZE(size); //申请空间最少MIN_SIZE_ALIGNED if(size < MIN_SIZE_ALIGNED) { size = MIN_SIZE_ALIGNED; } if (size > MEM_SIZE_ALIGNED) { return NULL; } /* protect the heap from concurrent access */ sys_mutex_lock(&mem_mutex); //从lfree开始遍历,找到第一个长度大于size的空闲内存块 for( ptr = (mem_size_t)((u8_t *)lfree - ram); ptr < MEM_SIZE_ALIGNED - size; ptr = ((struct mem *)(void *)&ram[ptr])->next ) { mem = (struct mem *)(void *)&ram[ptr];//取得内存块管理结构体mem if ( (!mem->used) && (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size ) {//未使用且空间大于等于用户要申请的size,否则for循环到下一个内存块管理结构体mem if( mem->next - (ptr + SIZEOF_STRUCT_MEM) >= (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED) )//判断分配给用户size后剩下的内存是否还能组成一个最小的块, {//能 ptr2 = ptr + SIZEOF_STRUCT_MEM + size;//分配后剩余空间偏移地址 //在ptr2处构造mem结构体管理/分配后剩余空间 mem2 = (struct mem *)(void *)&ram[ptr2]; mem2->used = 0;//未用 mem2->next = mem->next;//将新空闲快插入链表中 mem2->prev = ptr; mem->next = ptr2;//更新前面的mem,mem的next指向mem2 mem->used = 1;//已用 if (mem2->next != MEM_SIZE_ALIGNED) //如果mem2的下一个不是最后一个 { //mem2下面的管理结构体的prev要指向mem2即ptr2 ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2; }//增加全局量 MEM_STATS_INC_USED(used, (size + SIZEOF_STRUCT_MEM)); } else //不能组成最小的块 { //则全部分配给用户,标记已用 mem->used = 1; MEM_STATS_INC_USED(used, mem->next - (mem_size_t)((u8_t *)mem - ram));//增加全局量 } if (mem == lfree) //如果分配的是lfree指向的内存块则还要调整lfree { struct mem *cur = lfree; while (cur->used && cur != ram_end) { cur = (struct mem *)(void *)&ram[cur->next]; }//遍历找到下一个位置最低的空闲块更新lfree lfree = cur; } sys_mutex_unlock(&mem_mutex);//释放互斥量 return (u8_t *)mem + SIZEOF_STRUCT_MEM;//偏移掉管理结构体返回给用户 } } MEM_STATS_INC(err);//从for循环遍历也找不到长度大于size的空闲内存块,标记出错 sys_mutex_unlock(&mem_mutex);//释放互斥量 return NULL; }第一次mem_malloc 假设全部被malloc
3. mem_free 释放是根据传入的地址做SIZEOF_STRUCT_MEM偏移找到mem结构体,由于malloc的时候已经建立了next和prev的值所以只用改写used参数为就表示释放,最后还要判断能否和mem结构体前后的mem结构体做合并,如果能合并则要再次改写next和prev值
void mem_free(void *rmem) { struct mem *mem; //合法性判断 if (rmem == NULL) { return; } if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) { SYS_ARCH_DECL_PROTECT(lev); /* protect mem stats from concurrent access */ SYS_ARCH_PROTECT(lev); MEM_STATS_INC(illegal); SYS_ARCH_UNPROTECT(lev); return; } //做SIZEOF_STRUCT_MEM偏移找到mem结构体地址 mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM); mem->used = 0; //若释放的地址比lfree低则要更新lfree if (mem < lfree) { /* the newly freed struct is now the lowest */ lfree = mem; } //合并检查 plug_holes(mem); } static void plug_holes(struct mem *mem) { struct mem *nmem; struct mem *pmem; //找到mem的下一个mem管理结构体地址,并判断是否空闲 nmem = (struct mem *)(void *)&ram[mem->next]; if (mem != nmem && nmem->used == 0 && (u8_t *)nmem != (u8_t *)ram_end) { //空闲则做合并 if (lfree == nmem) { lfree = mem; } mem->next = nmem->next; ((struct mem *)(void *)&ram[nmem->next])->prev = (mem_size_t)((u8_t *)mem - ram); } //找到mem的上一个mem管理结构体地址,并判断是否空闲 pmem = (struct mem *)(void *)&ram[mem->prev]; if (pmem != mem && pmem->used == 0) {//空闲则做合并 if (lfree == mem) { lfree = pmem; } pmem->next = mem->next; ((struct mem *)(void *)&ram[mem->next])->prev = (mem_size_t)((u8_t *)pmem - ram); } }假如在上图的基础上释放size2则只需把size2的used改成0,因为size2的上下都不能合并,如下图
假如在全部都被malloc的情况下先free size1 再free size3,最后free size2则需要先合并下面相邻的,再合并上面相邻的。并更新包在合并内存快外面的mem结构体里面的next prev