lwIP源代码分析1-------内存管理模块的分析

因为lwIP主要用于嵌入式系统,内存要求比较高,所以要对那些小对象进行池化之类的处理来加快分配速度,减少内存碎片产生。

lwIP中主要有memp.h, memp_std.h, memp.c, mem.h, mem.c几个类组成了内存管理模块。

 

memp.c

动态内存池管理器, lwip拥有各种不同的内存池来为各个模块的小对象分配内存。

一个内存池主要有name,description,number(内存池里的内存节点的数目)和size(内存池里的内存节点的大小)

内存节点的struct描述:

struct memp {
  struct memp *next; // 指向下一个内存节点的指针
#if MEMP_OVERFLOW_CHECK // 如果进行内存溢出检测,
  const char *file; // 发生溢出时调用函数的文件名
  int line; // 发生溢出时调用函数的行号
#endif /* MEMP_OVERFLOW_CHECK */
};

 

lwip中各类内存池的描述在memp_std.h文件中:

该文件主要由三种内存池组成: 1. LWIP_MEMPOOL(标准的内存池) 2. LWIP_MALLOC_MEMPOOL(被mem.c中mem_malloc使用的内存池,需要在lwippools.h自定义不同大小的内存池,稍后会讲到) 3. LWIP_PBUF_MEMPOOL(PBUF的内存池)。其中2,3两种内存池均是通过调用第一种内存池来实现的,所以我们来看下第一种内存池,也就是lwip的标准内存池。

我们来看一个TCP_PCB的内存池定义:

LWIP_MEMPOOL(TCP_PCB,        MEMP_NUM_TCP_PCB,         sizeof(struct tcp_pcb),        "TCP_PCB")

其中TCP_PCB是内存池名称,MEMP_NUM_TCP_PCB是节点的数目,sizeof(struct tcp_pcb)是每个节点大小, "TCP_PCB"是内存池的描述。

而在memp.c中通过不断定义这些描述(宏)来保存内存池中各种不同的信息到相应的结构中去:

1. 通过各种内存池的唯一的名称定义一个enum,并且在最后插入MEMP_MAX来得到内存池的总数。

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

 

2.定义数组memp_size[MEMP_MAX]存放每个内存池的节点大小。

 /** This array holds the element sizes of each pool. */
#if !MEM_USE_POOLS && !MEMP_MEM_MALLOC
static
#endif
const u16_t memp_sizes[MEMP_MAX] = {
#define LWIP_MEMPOOL(name,num,size,desc)  LWIP_MEM_ALIGN_SIZE(size),
#include "lwip/memp_std.h"
};

 

3. 定义数组memp_num[MEMP_MAX]存放每个内存池的节点数目。

/** This array holds the number of elements in each pool. */
static const u16_t memp_num[MEMP_MAX] = {
#define LWIP_MEMPOOL(name,num,size,desc)  (num),
#include "lwip/memp_std.h"
};

 

4. 定义数组memp_num[MEMP_MAX]存放每个内存池的描述。

/** This array holds a textual description of each pool. */
#ifdef LWIP_DEBUG
static const char *memp_desc[MEMP_MAX] = {
#define LWIP_MEMPOOL(name,num,size,desc)  (desc),
#include "lwip/memp_std.h"
};
#endif /* LWIP_DEBUG */

 

/** This array holds a textual description of each pool. */
#ifdef LWIP_DEBUG
static const char *memp_desc[MEMP_MAX] = {
#define LWIP_MEMPOOL(name,num,size,desc)  (desc),
#include "lwip/memp_std.h"
};
#endif /* LWIP_DEBUG */

 

5. 定义数组memp_memory,这个数组所占有的内存就是所有内存池用的实际内存块。

数组长度为各个池的需要的内存总和,即每个内存池+ ( (num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size) ) ),

其中num为节点数目,MEMP_SIZE为每个节点结构所需要的额外内存,size为内存池提供给上层用户的内存大小。

这些大小都是要对齐的,还有个MEM_ALIGNMENT - 1是为了对齐数组memp_memory,因为定义的memp_memory是不

一定对齐的,最坏的情况就要MEM_ALIGNMENT - 1。

/** This is the actual memory used by the pools. */
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"
];

 

内存对齐的概念我想大家都懂,这儿就不多做解释了,在lwip的mem.h头文件定义了下面两个宏来进行size和内存地址的对齐。

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

#ifndef LWIP_MEM_ALIGN
#define LWIP_MEM_ALIGN(addr) ((void *)(((mem_ptr_t)(addr) + MEM_ALIGNMENT - 1) & ~(mem_ptr_t)(MEM_ALIGNMENT-1)))
#endif

 

mem.c:

如果使用pool的话就是运用内存池管理器进行内存管理。

否则的话主要实现了一个实现了一个类似于C malloc的heap的轻量级内存管理器。

 

 

下面开始讲述每个函数的功能,工作原理和要点:

memp.h memp.c

memp_init(): 初始化每个内存池,通过struct memp作为节点以单链表的形式串联起来。

memp_malloc(): 从相应的内存池中取出内存。

memp_free(): 归还内存到相应的内存池,需要检测溢出。

memp_overflow_init(): 溢出检测的初始化,主要是在用户申请的内存区域前后插入一些填充符0xcd。

memp_overflow_check_element(): 节点溢出检测,在用户申请的内存区域前后检查填充符0xcd是否被覆盖,是则溢出。

memp_overflow_check_all(): 所有节点溢出检测,安全,但是相对慢。

 

一个内存池的一个结点的内存分配情况:

------------------------------------------------------------------------------------------------------------------------------------

struct memp所占用的内存 + check_overflow_before_region + 用户申请的内存 +check_overflow_after_region

------------------------------------------------------------------------------------------------------------------------------------

 

memp_memory的内存分配情况:

------------------------------------------------------------------------------------------------------------------------------------

 对齐地址的内存空间(0->MEM_ALIGNMENT - 1) + 各个内存池所占内存

------------------------------------------------------------------------------------------------------------------------------------

 

memp_std.h:

防止多次定义错误,因为memp.c使用#include memp_std.h并且定义以下的宏的技巧,所以每次需要undef这些宏.

 

mem.h mem.c

#if MEM_USE_POOLS:

mem_malloc: 从相应的提供给malloc用的不同size的pool中取一个合适的一个,并且将这个pool的size保存到相应的结构memp_malloc_helper中。

可以在lwippools.h自定义pool,详情请见代码memp_std.h和memp.h文件的注释:

 LWIP_MALLOC_MEMPOOL_START
 LWIP_MALLOC_MEMPOOL(20, 256)
 LWIP_MALLOC_MEMPOOL(10, 512)
 LWIP_MALLOC_MEMPOOL(5, 1512)
 LWIP_MALLOC_MEMPOOL_END

 

mem_free: 释放到相应的内存池中, 取出memp_malloc_helper中的size放入相应的内存池中去。

 

#else /* MEM_USE_POOLS: */

实现了一个类似于C malloc的heap的轻量级内存管理器。

/* 采用索引双链表的形式来管理heap */
/* 节点结构 */
struct mem {
  /** index (-> ram[next]) of the next struct */
  mem_size_t next; // 下一个struct的索引
  /** index (-> ram[next]) of the next struct */
  mem_size_t prev; // 上一个struct的索引
  /** 1: this area is used; 0: this area is unused */
  u8_t used; // 是否被使用
};

mem_init: 初始化heap,并且设置lfree为最低地址的空闲节点,以加快搜索速度。

mem_malloc: 从heap上分配用户所需size的内存。从lfree节点开始搜索空闲并且可以容纳SIZEOF_STRUCT_MEM + size的空间,

如果找到这样的节点mem,则分为两种情况,如果可容纳的空间可以容纳SIZEOF_STRUCT_MEM + size + (SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED),则说明可以新建一个空闲节点,于是将建立新的空闲节点并且插入到mem和mem->next之间,将mem的used标志为1,否则的话不建立节点,只是设置mem的used标志为1。

mem_free: 释放的时候会改used标志为0,表示空闲,并且调用plug_holes()函数来进行前后合并空闲空间。

mem_calloc: 使用mem_malloc申请size×count大小的地址,并且初始化清0所分配的内存。

mem_realloc: 重新分配new_size的内存,lwip目前不支持扩大size的情况。

如果当前节点后面的那个节点也是free的话,那么可以合并多余剩下的节点和后面的那个free节点。

如果不是空闲的则看看剩余的空间是否足够新建一个空闲节点,能则建之,并将其插入到链表中去。 

 

相关文件的代码注释:

 

memp.h

 

memp.c

 

memp_std.h

 

mem.h

 

mem.c

 

终于讲完了,希望有兴趣的同学来联系我,kaka11.chen@gmail.com,下篇文章会讲链路层模块,敬请期待。

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值