LWIP学习笔记(1)LWIP内存管理内存池

动态内存管理本质是对一大块内存(可以理解为数组)分配和释放的管理,lwip提供了2种动态内存管理策略内存池pool、内存堆heap

内存池memp.c/h

系统只能为用户分配几个固定大小的内存块,优点是比较快,不会产生内存碎片,缺点是产生内存浪费,适合对那些固定数据结构进行空间分配,比如TCP首部,IP首部等
下面是与内存池管理相关数据结构

名称类型所在文件描述
memp_t枚举类型memp.h为每种pool定义编号index
memp_tab[]全局型指针数组memp.c分别指向每类pool中的第一个pool
memp_sizes[]全局型数组memp.c每类pool中单个pool所占字节数
memp_num[]全局型数组memp.c每类pool中pool的个数
memp_desc[]全局型指针数组memp.c指向没pool的描述字符串
memp_memory[]全局型数组memp.c所有pool所占内存总和

memp.h 只定义了一个枚举类型,其他宏默认为0不会被编译

memp_t

typedef enum {
#define LWIP_MEMPOOL(name,num,size,desc)  MEMP_##name,
#include "lwip/memp_std.h"
  MEMP_MAX
} memp_t;

首先是个宏定义,##是连接符,表示吧MEMP_和宏定义中的name连接起来,然后include一个头文件,表示把这个头文件内容复制到下面,看下memp_std.h头文件

...
#if LWIP_RAW
LWIP_MEMPOOL(RAW_PCB,        MEMP_NUM_RAW_PCB,         sizeof(struct raw_pcb),        "RAW_PCB")
#endif /* LWIP_RAW */

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

这个头文件都是一些宏定义表示不同类型的pool,比如
LWIP_MEMPOOL(RAW_PCB, MEMP_NUM_RAW_PCB, sizeof(struct raw_pcb),"RAW_PCB")
这种pool类型是RAW_PCB,有MEMP_NUM_RAW_PCB个,每一个大小sizeof(struct raw_pcb),描述符是"RAW_PCB",注意这个头文件没有#ifndef、#define、#endif防止重复include的条件编译,所以它可以被多次include,而且最后#undef LWIP_MEMPOOL表示取消LWIP_MEMPOOL这个宏定义,以便后面重新宏定义。
结合memp_t的宏定义则memp_t最终会变成如下形式

typedef enum {
MEMP_RAW_PCB,
MEMP_UDP_PCB,
MEMP_TCP_PCB,
MEMP_TCP_PCB_LISTEN,
MEMP_TCP_SEG,
...
  MEMP_MAX
} memp_t;

这句是为每种类型pool编个号index,其中MEMP_MAX,表示有多少种类型pool

memp_sizes[]

static const u16_t memp_sizes[MEMP_MAX] = {
#define LWIP_MEMPOOL(name,num,size,desc)  LWIP_MEM_ALIGN_SIZE(size),
#include "lwip/memp_std.h"
};

同理,memp_sizes[]最终形式如下

static const u16_t memp_sizes[MEMP_MAX] = {
LWIP_MEM_ALIGN_SIZE(sizeof(struct raw_pcb)),
LWIP_MEM_ALIGN_SIZE(sizeof(struct udp_pcb)),
LWIP_MEM_ALIGN_SIZE(sizeof(struct tcp_pcb)),
LWIP_MEM_ALIGN_SIZE(sizeof(struct tcp_pcb_listen)),
LWIP_MEM_ALIGN_SIZE(sizeof(struct tcp_seg)),
...
};

LWIP_MEM_ALIGN_SIZE是如下宏定义,表示MEM_ALIGNMENT字节对齐后向上取整

#define LWIP_MEM_ALIGN_SIZE(size) (((size) + MEM_ALIGNMENT - 1) & ~(MEM_ALIGNMENT-1))

memp_num[]同理

static const u16_t memp_num[MEMP_MAX] = {
#define LWIP_MEMPOOL(name,num,size,desc)  (num),
#include "lwip/memp_std.h"
};

如下

static const u16_t memp_num[MEMP_MAX] = {
MEMP_NUM_RAW_PCB,
MEMP_NUM_UDP_PCB,
MEMP_NUM_TCP_PCB,
MEMP_NUM_TCP_PCB_LISTEN,
MEMP_NUM_TCP_SEG,
...
};

memp_desc[]同理

static const char *memp_desc[MEMP_MAX] = {
#define LWIP_MEMPOOL(name,num,size,desc)  (desc),
#include "lwip/memp_std.h"
};

如下

static const char *memp_desc[MEMP_MAX] = {
("RAW_PCB"),
("UDP_PCB"),
("TCP_PCB"),
("TCP_PCB_LISTEN"),
...
};

最后memp_memory[]是按全部内存池所占容量的总和开辟的一块连续内存(就是定义了一个数组)

static u8_t memp_memory[MEM_ALIGNMENT - 1 
#define LWIP_MEMPOOL(name,num,size,desc) + ( (num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size) ) )
#include "lwip/memp_std.h"
];

最终如下

static u8_t memp_memory[MEM_ALIGNMENT - 1 
+ ( (MEMP_NUM_RAW_PCB) * (MEMP_SIZE + MEMP_ALIGN_SIZE(sizeof(struct raw_pcb)) ) )
+ ( (MEMP_NUM_UDP_PCB) * (MEMP_SIZE + MEMP_ALIGN_SIZE(sizeof(struct udp_pcb)) ) )
+ ...
];

MEM_ALIGNMENT - 1字节对齐目的
MEMP_SIZE表示在每个pool头不预留的空间,根据宏定义有2种取值,下面会分别画出2种取值的图示,这里是为0

在这里插入图片描述
MEMP_ALIGN_SIZE宏将size字节对齐后向上取整

memp_tab[]是指针数组,里面每个元素指向各自类型pool的第一个空闲pool

struct memp {
  struct memp *next;
};
static struct memp *memp_tab[MEMP_MAX];
内存池管理函数

1. memp_init
初始化要实现的功能是按照不同的类型把每个类型下的pool依次用链表串起来,memp_tab用来存放头指向每种pool下的第一个空闲pool,每种类型最后一个空闲pool指向NULL,本质是构造MEMP_MAX条单向链表,只不过这些单向链表一开始地址是连续的

void memp_init(void)
{
  struct memp *memp;
  u16_t i, j;
  //memp指向memp_memory对齐后地址
  memp = (struct memp *)LWIP_MEM_ALIGN(memp_memory);
  /* for every pool: */
  for (i = 0; i < MEMP_MAX; ++i) //循环对每种pool初始化
  {
    memp_tab[i] = NULL;//开始没有pool,所以指向NULL
    /* create a linked list of memp elements */
    for (j = 0; j < memp_num[i]; ++j) 
    {
      memp->next = memp_tab[i];//新入pool的next指向的之前的第一个pool地址
      memp_tab[i] = memp; //插入新pool到首部
      memp = (struct memp *)(void *) //偏移到下一个新pool
      ((u8_t *)memp + MEMP_SIZE + memp_sizes[i]);
    }
  }
}

用图表示如下(下面每种类型pool只画了3个)
在这里插入图片描述
把每个pool详细内容表示出来,用另一种形式画出来如下,分别画出MEMP_SIZE为0和不为0情况,memp_tab指向链表中的MEMP_SIZE当然要么全为0要么全不为0,这里为方便个画一种。(下面每种类型pool只画了2个)
在这里插入图片描述
2. memp_malloc
是把memp_tab指向的pool分配给用户并指向下一个链表的pool,本质是取出链表出头节点

void * memp_malloc(memp_t type) //输入参数是要分配的pool类型index
{
  struct memp *memp;
  SYS_ARCH_DECL_PROTECT(old_level);//声明一个临界区保护变量
  SYS_ARCH_PROTECT(old_level);//进入临界区
  memp = memp_tab[type];//根据类型index取出链表头
  
  if (memp != NULL) {//不为空说明还此类型还有pool
    memp_tab[type] = memp->next;//链表头指向下一个pool
    MEMP_STATS_INC_USED(used, type);//增加内存池分配相关统计量
    memp = (struct memp*)(void *)((u8_t*)memp + MEMP_SIZE);//偏移出预留空间作为返回给用户
  } else {
    MEMP_STATS_INC(err, type);//增加内存池分配出错统计量
  }
  SYS_ARCH_UNPROTECT(old_level);//退出临界区
  return memp;//返回分配的地址给用户
}

3. memp_free
释放本质是把用户交给你的地址插入链表头节点

void memp_free(memp_t type, void *mem)//要知道释放的类型和地址
{
  struct memp *memp;
  SYS_ARCH_DECL_PROTECT(old_level);//声明一个临界区保护变量

  if (mem == NULL) {//null直接返回
    return;
  }
  //得到pool的起始地址,并用memp指向pool的起始地址,再构造struct memp结构体
  memp = (struct memp *)(void *)((u8_t*)mem - MEMP_SIZE);

  SYS_ARCH_PROTECT(old_level);//进入临界区
  MEMP_STATS_DEC(used, type); //减少内存池分配相关统计量
  //将pool插入memp_tab[type]头部
  memp->next = memp_tab[type]; //memp->next指向原来的头部
  memp_tab[type] = memp;//memp_tab指向新的头部

  SYS_ARCH_UNPROTECT(old_level);//退出临界区
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值