2.LwIP_内存管理

LwIP有两种内存管理策略,它们分别是内存堆和内存池。

1、内存堆

1.1 相关特性

内存堆是什么:

内存堆就是动态分配内存,从低地址开始查找找到合适的内存后返回指针,并将多余的内存与临近的内存进行合并(first fit算法)。

内存堆的由来:

1、申请一个名为ram_heap的大数组,模拟C库的malloc和free的实现过程,对大数组进行操作。LwIP默认选择该方式。

2、动态申请内存池,以此为基础作为内存堆的空间。一般不使用,配置宏MEM_USE_POOLS、MEMP_USE_CUSTOM_POOLS可使能。

3、使用C库的malloc来实现。一般不使用该情况,配置宏MEM_LIBC_MALLOC可使能。

相关解释在mem.c中有如下解释:

内存堆的对齐:

LWIP_MEM_ALIGN_SIZE宏用于对齐,它的对齐字节数由宏MEM_ALIGNMENT所确定,在默认情况下MEM_ALIGNMENT = 4,因此是4字节对齐。 

以下内存对齐描述都认为MEM_ALIGNMENT = 4,即:4字节对齐。

对于最小分配内存大小,它所占的空间由宏MIN_SIZE_ALIGNED表示,在默认情况下MIN_SIZE = 12,因此对齐后最小分配内存大小 = 12字节。

对于内存控制块,它所占的空间用宏SIZEOF_STRUCT_MEM表示,sizeof(struct mem)的范围是5~7,因此经过对齐后应为8字节。

对于内存堆的可用最大空间,它所占的空间由宏MEM_SIZE_ALIGNED表示,在默认情况下MEM_SIZE = 30*1024,因此经过对齐后内存堆的空间应为30K字节。

对于内存堆的数组名与空间大小,它由宏LWIP_DECLARE_MEMORY_ALIGNED来确定,最终的结果为注释部分

内存堆相关指针:

u8_t* ram:指向内存堆对齐后起始地址,即:ram_heap[0]的地址。

struct mem* ram_end:指向系统最后一个内存块,这代表内存堆的结尾,是不可用的空间。

struct mem* lfree:指向当前系统具有最低地址的空闲内存块,这是动态变化的值。

内存堆的结构:

内存堆由内存控制块和可用内存两部分构成,当申请内存堆后,返回的地址是可用内存的首地址。

具体框图如下:

1.2 相关函数

内存堆的管理函数存放在mem.c中,关键函数有init、malloc、free三个,分别对应初始化、申请、释放。

1.2.1 mem_init

控制块结构体:

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 :已使用
#if MEM_OVERFLOW_CHECK
  /** this keeps track of the user allocation size for guard checks */
  mem_size_t user_size;
#endif
};

函数功能:

初始化LwIP的内存堆,由lwip_init调用。

函数结构:

1、初始化raw指针指向数组首地址

2、在数组首地址处生成一个控制块,并代表整个内存堆为空。

        mem->next = MEM_SIZE_ALIGNED; 

        mem->used = 0;

3、在数组尾部生成一个控制块,并代表内存堆的结尾部分。

4、将lfree指向raw,因为在初始化时内存堆全为空。

函数实现:

/**
 * Zero the heap and initialize start, end and lowest-free
 */
void
mem_init(void)
{
  struct mem *mem;

  LWIP_ASSERT("Sanity check alignment",
              (SIZEOF_STRUCT_MEM & (MEM_ALIGNMENT - 1)) == 0);

  //1.初始化raw指向数组头
  ram = (u8_t *)LWIP_MEM_ALIGN(LWIP_RAM_HEAP_POINTER);//ram指向内存堆的首地址
  
  //2.初始化头控制块,空间的开头
  mem = (struct mem *)(void *)ram;	//mem是控制块指针,指向第一个控制块
  mem->next = MEM_SIZE_ALIGNED;		//MEM_SIZE_ALIGNED为内存堆的最大空间,next指向它代表整个空间空闲
  mem->prev = 0;
  mem->used = 0;					//used = 0代表本空间可用
  
  //3.初始化尾控制块,空间的结尾
  ram_end = ptr_to_mem(MEM_SIZE_ALIGNED);	//ram_end是控制块指针,指向最末尾的位置,代表内存堆空间已用完
  ram_end->used = 1;						//ram_end之后虽然还有一些空间,但这些空间用作对齐,因此used = 1代表不可用
  ram_end->next = MEM_SIZE_ALIGNED;										
  ram_end->prev = MEM_SIZE_ALIGNED;
  MEM_SANITY();

  //4.初始化lfree指向最低地址的空闲内存块
  lfree = (struct mem *)(void *)ram;		//初始化时全空闲,因此lfree指向数组头ram

  MEM_STATS_AVAIL(avail, MEM_SIZE_ALIGNED);

  if (sys_mutex_new(&mem_mutex) != ERR_OK) {
    LWIP_ASSERT("failed to create mem_mutex", 0);
  }
}

初始化后指针与空间分布如下: 

1.2.2 mem_malloc

函数功能:

在内存堆中开辟指定大小的空间。

函数结构:

1、对申请大小进行相关处理,如:对齐、大小应至少为最小内存堆大小、大小应小于最大的大小

2、使用first fit算法查找空闲内存堆,这是一个循环遍历的查找

first fit算法:

1、整体框架是令ptr指向lfree,之后通过next遍历整个内存堆。代码如下:

for (ptr = mem_to_ptr(lfree); ptr < MEM_SIZE_ALIGNED - size;
     ptr = ptr_to_mem(ptr)->next) {	

    //相关处理代码
}

在遍历过程中,如果ptr<剩余空间,那么该循环则会退出。内存框图如下:

2、在循环中,首先要将ptr转为struct mem格式,从而对控制块的used位进行判断。如果该块未被使用并且内存足够的话,则进行相应操作。 

for(){
    mem = ptr_to_mem(ptr);	//转为控制块进行操作
    if ((!mem->used) &&
          (mem-&g
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值