LWIP学习笔记(2)LWIP内存管理内存堆

本文围绕LWIP内存堆管理展开,用户可向系统申请分配有大小限制的内存块。介绍了与内存堆管理相关的数据结构,如mem结构体,解释其成员含义。还说明了内存堆的定义、初始化,以及内存块的分配和释放操作,包括合并逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

内存堆mem.c/h

用户可以向系统申请分配任意大小内存块(有大小限制,最小不能小于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结构体,内存堆函数实现,内存堆数据定义

mem结构体(内存块管理结构体)

在这里插入图片描述
这里解释下
next 下一个内存块相对于内存堆首地址偏移量
prev 上一个内存块相对于内存堆首地址偏移量
used 1表示mem结构体管理的内存块已分配,0表示未被分配

内存堆定义

在这里插入图片描述
用户定义的可用内存堆大小是MEM_SIZE,在lwipopt.hopt.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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值