因为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节点。
如果不是空闲的则看看剩余的空间是否足够新建一个空闲节点,能则建之,并将其插入到链表中去。
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节点。
如果不是空闲的则看看剩余的空间是否足够新建一个空闲节点,能则建之,并将其插入到链表中去。