Skip to content

内存管理

image-20240702193327764

image-20240702194053751

选择

image-20240702214453671

内存堆

lwIP内存堆是一种可变长分配策略,可以随意申请任意大小的内存

image-20240702194310034

有三个函数

  • mem_init()内存堆的初始化
  • mem_malloc()申请内存堆
  • mem_free()释放内存堆

主要的实现在mem.c文件里面

c
/**
 * The heap is made up as a list of structs of this type.
 * This does not have to be aligned since for getting its size,
 * we only use the macro SIZEOF_STRUCT_MEM, which automatically aligns.
 */
//① ********************** 内存块控制块结构 *********************************************
struct mem {
  /** index (-> ram[next]) of the next struct */
  mem_size_t next;   // 指向下一个节点
  /** index (-> ram[prev]) of the previous struct */
  mem_size_t prev;   // 指向上一个节点
  /** 1: this area is used; 0: this area is unused */
  u8_t used;         // 描述内存块是否可用 0 :未使用;1 :已使用
};

一个描述符结构体

c
/** All allocated blocks will be MIN_SIZE bytes big, at least!
 * MIN_SIZE can be overridden to suit your needs. Smaller values save space,
 * larger values could prevent too small blocks to fragment the RAM too much. */
//② ********************** 最小分配内存 ******************
#ifndef MIN_SIZE
#define MIN_SIZE             12
#endif /* MIN_SIZE */
c
/** Calculate memory size for an aligned buffer - returns the next highest
 * multiple of MEM_ALIGNMENT (e.g. LWIP_MEM_ALIGN_SIZE(3) and
 * LWIP_MEM_ALIGN_SIZE(4) will both yield 4 for MEM_ALIGNMENT == 4). 对齐计算
 */
#ifndef LWIP_MEM_ALIGN_SIZE
#define LWIP_MEM_ALIGN_SIZE(size) (((size) + MEM_ALIGNMENT - 1U) & ~(MEM_ALIGNMENT-1U))
#endif

/* some alignment macros: we define them here for better source code layout */
//③ ********************** 对齐操作 ******************
#define MIN_SIZE_ALIGNED     LWIP_MEM_ALIGN_SIZE(MIN_SIZE)       //最小分配内存大小对齐 --12字节
#define SIZEOF_STRUCT_MEM    LWIP_MEM_ALIGN_SIZE(sizeof(struct mem))  // 内存控制块对齐---8字节
#define MEM_SIZE_ALIGNED     LWIP_MEM_ALIGN_SIZE(MEM_SIZE)  // 内存堆对齐--lwipopts.h文件下MEM_SIZE配置项
c
/** the heap. we need one struct mem at the end and some room for alignment */
//④ ********************** 内存堆定义--大数组 ******************
//这一个使用的对齐方式是直接请求一个比对齐小1byte的区域
//之后可以使用这一个区域里面的对齐的位置
LWIP_DECLARE_MEMORY_ALIGNED(ram_heap, MEM_SIZE_ALIGNED + (2U * SIZEOF_STRUCT_MEM));
//    |
//    |
// ram_heap[(((MEM_SIZE_ALIGNED + (2U * SIZEOF_STRUCT_MEM)) + MEM_ALIGNMENT - 1U))]
#define LWIP_RAM_HEAP_POINTER ram_heap   //进行一次重命名

使用一个大数组作为可以使用的内存

c
//⑤ ********************** 内存堆所需要的指针变量 ******************
/** pointer to the heap (ram_heap): for alignment, ram is now a pointer instead of an array */
static u8_t *ram;                       // 指向内存堆对齐后起始地址
/** the last entry, always unused! */
static struct mem *ram_end;             // 指向系统最后一个内存块
c
/** pointer to the lowest free block, this is used for faster search */
//⑥ ********************** 指向当前系统具有最低地址的空闲内存块 ******************
static struct mem * LWIP_MEM_LFREE_VOLATILE lfree;

image-20240702220902178

image-20240702224247260

释放的时候实际是吧记录当前空闲的lfree进行更改, 之后使用plug_holes函数判断一下前后的块是不是可以进行合并, 是的话进行合并

内存池

在使用的时候把内存分成多个大小相同的内存块, 使用链表进行连接, 使用的时候不需要切割, 直接进行分配, 释放的时候也不需要进行合并相邻的内存块

优点: 分配的速度快, 防止内存碎片化, 回收便捷

缺点: 浪费, 使用大内存的时候可能申请失败

image-20240703194938449

memp_priv.h

定义结构体memp, 实际是一个链表项, 只有一个next成员

memp_desc, 记录内存池的每一个内存块的大小, 数量, 基地址, 以及第一个空闲块的链表

image-20240703195734902

mem_std.h

根据使用的不同的模块, 初始化不同大小的内存池, 一般在lwipopts.h文件里面启用不同的模块

c
#define MEMP_NUM_UDP_PCB        6

#if LWIP_UDP
LWIP_MEMPOOL(UDP_PCB,        MEMP_NUM_UDP_PCB,         sizeof(struct udp_pcb),        "UDP_PCB")
#endif /* LWIP_UDP */

memp.h

c
/** Create the list of all memory pools managed by memp. MEMP_MAX represents a NULL pool at the end */
typedef enum {
#define LWIP_MEMPOOL(name,num,size,desc)  MEMP_##name,
#include "lwip/priv/memp_std.h"
  MEMP_MAX
} memp_t;

可以转换为

c
/** Create the list of all memory pools managed by memp. MEMP_MAX represents a NULL pool at the end */
typedef enum {
	MEMP_UDP_PCB,
    ...
  	MEMP_MAX
} memp_t;

mem.c

c
#define LWIP_DECLARE_MEMORY_ALIGNED(variable_name, size) u8_t variable_name[LWIP_MEM_ALIGN_BUFFER(size)]

#define LWIP_MEM_ALIGN_BUFFER(size) (((size) + MEM_ALIGNMENT - 1U))    

#define LWIP_MEMPOOL_DECLARE_STATS_INSTANCE(name) 
#define MEMP_SIZE          (LWIP_MEM_ALIGN_SIZE(sizeof(struct memp)) + MEM_SANITY_REGION_BEFORE_ALIGNED)
    
#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEMPOOL_DECLARE(name,num,size,desc)
#define LWIP_MEMPOOL_DECLARE(name,num,size,desc) \
  LWIP_DECLARE_MEMORY_ALIGNED(memp_memory_ ## name ## _base, ((num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size)))); \
    \
  LWIP_MEMPOOL_DECLARE_STATS_INSTANCE(memp_stats_ ## name) \
    \
  static struct memp *memp_tab_ ## name; \
    \
  const struct memp_desc memp_ ## name = { \
    DECLARE_LWIP_MEMPOOL_DESC(desc) \
    LWIP_MEMPOOL_DECLARE_STATS_REFERENCE(memp_stats_ ## name) \
    LWIP_MEM_ALIGN_SIZE(size), \
    (num), \
    memp_memory_ ## name ## _base, \
    &memp_tab_ ## name \
  };

展开以后

c
#define LWIP_MEMPOOL_DECLARE(UDP_PCB, 6, sizeof(struct udp_pcb), "UDP_PCB") \
  u8_t memp_memory_UDP_PCB_base[(6 * (sizeof(struct memp) + 16 + sizeof(struct memp))))]; \
  static struct memp *memp_tab_UDP_PCB; \
  const struct memp_desc memp_UDP_PCB = { \
    UDP_PCB, \
    6, \
    memp_memory_UDP_PCB_base, \
    &memp_tab_UDP_PCB \
  };
c
const struct memp_desc *const memp_pools[MEMP_MAX] = {
#define LWIP_MEMPOOL(name,num,size,desc) &memp_ ## name,
#include "lwip/priv/memp_std.h"
};
    | 
    | 
    | 展开后的代码
    | 
    V 
const struct memp_desc *const memp_pools[MEMP_MAX] = {
  &memp_TCPIP_MSG_API,
  &memp_TCPIP_MSG_INPKT,
  .........
};

重要的函数

c
memp_init(); // 初始化
memp_malloc(); //获取某一个类型的内存池
memp_free(); //释放内存池
c
void
memp_init(void)
{
  u16_t i;
  /* for every pool: */
  for (i = 0; i < LWIP_ARRAYSIZE(memp_pools); i++) {
    memp_init_pool(memp_pools[i]);
  }
}
c
void
memp_init_pool(const struct memp_desc *desc)
{
  int i;
  struct memp *memp;

  *desc->tab = NULL;
  memp = (struct memp *)LWIP_MEM_ALIGN(desc->base); //获取一下地址
    
  /* create a linked list of memp elements */
  for (i = 0; i < desc->num; ++i) {
    memp->next = *desc->tab;
    *desc->tab = memp;   //记录第一个空闲位置

    /* cast through void* to get rid of alignment warnings */
    memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + desc->size); //偏移到下一个位置
  }
}

desc->tab会指向最后一个, memp->next指向前一个

image-20240703214545092

image-20240703214725751

c
void *
memp_malloc(memp_t type)
{
  void *memp;
  LWIP_ERROR("memp_malloc: type < MEMP_MAX", (type < MEMP_MAX), return NULL;);
  memp = do_memp_malloc_pool(memp_pools[type]);

  return memp;
}

之后实际就是对链表的操作