本文在分析lwIP2.1.2协议栈存储池的基础之上给出了一种lwIP存储池动态申请的设计供感兴趣者参考。
一、lwIP存储池的总体说明
lwIP协议内核使用两种存储器管理方法,一种是存储器的堆栈管理,另一种是存储器的存储池管理。存储器堆栈管理就是对一个大数组的管理,存储池的管理就是对几个数组进行管理。存储池中的每一个数组都分成几个块,同一数组中的各块大小相同,不同数组中的块大小不同,每个数组的块的大小对应于lwIP核心中的某一个控制块的大小,当然lwIP核心的不同控制块大小不一致,因此每个大小的控制块对应于存储池中的某个数组。
存储器存储堆栈管理比较简单,只要一个数据结构即可对其管理。存储池的管理比较复杂,要使用几个数据结构。存储池对单个数组使用单向链结构管理,使用的数据结构如下:
struct memp {
struct memp *next;
};
memp只能单向寻找某个数组中的各块,不同数组的块的大小和数目都不同,为此定义另一个数据结构描述这些特性,即
struct memp_desc {
u16_t size; /** Element size */
u16_t num; /** Number of elements */
u8_t *base; /** Base address */
struct memp **tab; /** First free element of each pool. Elements form a linked list. */
};
其中,base为存储池中某个数组基地址, size为数组中每个块的大小, num为数组中块数,数组中第一个未使用块的控制块为tab;显然lwIP存储池的每个数组都要使用一个这个数据结构,描述lwIP存储池中所有数组就得用一个mem_desc数组,由于每个数组的描述符是单独申请的,所以用一个mem_desc指针数组管理存储池,指针数组的每个指针等于某个存储池中某个数组描述符的地址。对于某一应用来讲mem_desc数组大小固定,但是对不同应用mem_desc数组大小不同,为了适应不同应用使用如下方法定义数组:
const struct memp_desc *const memp_pools[MEMP_MAX] = {
#define LWIP_MEMPOOL(name,num,size,desc) &memp_ ## name,
#include "lwip/priv/memp_std.h"
};
显然这个数组是一个指针数组,数组中每一个元素就是一个memp_desc结构的指针,对应于某一个数组的描述符mem_desc的地址。
指针数组大小MEMP_MAX使用枚举类型定义方法获取,即
typedef enum {
#define LWIP_MEMPOOL(name,num,size,desc) MEMP_##name,
#include "lwip/priv/memp_std.h"
MEMP_MAX
} memp_t;
存储池中的数组和描述符使用宏定义方法定义,即
#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEMPOOL_DECLARE(name,num,size,desc)
#include "lwip/priv/memp_std.h"
二、lwIP存储池的数组和描述符宏定义分析
存储池中除了memp和memp_desc定义,其他定义均使用宏定义,下面分析存储池所用的数据类型或变量的宏定义。
-
枚举类型memp_t的宏定义分析
枚举类型memp_t的宏定义为
#define LWIP_MEMPOOL(name,num,size,desc) MEMP_##name,
#include "lwip/priv/memp_std.h"
MEMP_MAX
} memp_t;
memp_t枚举类型宏定义除最后一个元素MEMP_MAX外其他元素均由一个宏定义加一个包含文件形成,包含文件中遇到宏命令LWIP_MEMPOOL(name,num,size,desc)就用符号MEMP_name代替。根据memp_std.h包含文件和lwIP配置文件lwipopts.h可得如下枚举类型,即
typedef enum {
MEMP_RAW_PCB,//lwipopts.h文件中定义了LWIP_RAW,没有定义就没有该元素
MEMP_UDP_PCB,// lwipopts.h文件中定义了LWIP_UDP,没有定义就没有该元素
MEMP_TCP_PCB, // lwipopts.h文件中定义了LWIP_TCP,没有定义就没有该元素
MEMP_TCP_PCB_LISTEN,//同上
MEMP_TCP_SEG,//同上
…………
MEMP_PBUF,
MEMP_PBUF_POOL,
MEMP_MAX
} memp_t;
由此可见这种定义用户变量类型的方法具有很大灵活性,适用于开发工具使用或开发某一类应用使用,也可以把这定义方法称为函数定义法,即宏定义的不同输入产生宏定义的不同输出。另外,无论lwipopts.h如何定义枚举类型memp_t均有MEMP_PBUF、MEMP_PBUF_POOL和MEMP_MAX元素,也就是这几个元素不受lwipopts.h影响
2、存储池的描述符指针数组的宏定义分析
存储池的描述符指针数组memp_pools的宏定义为
const struct memp_desc *const memp_pools[MEMP_MAX] = {
#define LWIP_MEMPOOL(name,num,size,desc) &memp_ ## name,
#include "lwip/priv/memp_std.h"
};
memp_desc类型指针数组memp_pools的宏定义与枚举类型memp_t的宏定义比较相似,不同之处没有最后给定元素,另外遇到宏命令LWIP_MEMPOOL(name,num,size,desc)用符号&memp_name代替,其中memp_name应该是某处定义的memp_desc变量,否则出错。根据memp_std.h包含文件和lwIP配置文件lwipopts.h可得如下memp_pools数组,即
const struct memp_desc *const memp_pools[MEMP_MAX] = {
&memp_RAW_PCB,//lwipopts.h文件中定义了LWIP_RAW,没有定义就没有该元素
&memp_UDP_PCB,// lwipopts.h文件中定义了LWIP_UDP,没有定义就没有该元素
&memp_TCP_PCB, // lwipopts.h文件中定义了LWIP_TCP,没有定义就没有该元素
&memp_TCP_PCB_LISTEN,//同上
&memp_TCP_SEG,//同上
…………
&memp_PBUF,
&memp_PBUF_POOL,
};
3、
目录
存储池的描述符和各数组的宏定义为
#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEMPOOL_DECLARE(name,num,size,desc)
#include "lwip/priv/memp_std.h"
其中LWIP_MEMPOOL_DELCLARE(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 \
};
由此可见,在lwipopts.h文件中遇到宏命令LWIP_MEMPOOL(name,num,size,desc)需要用4个元素代替,即
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 \
};
1)数组宏命令分析
数组宏命令LWIP_DECLARE_MEMORY_ALIGNED(memp_memory_ ## name ## _base, ((num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size))))可以根据LWIP_DECLARE_MEMORY_ALIGNED的宏定义展开,其宏定义如下:
#define LWIP_DECLARE_MEMORY_ALIGNED(variable_name, size) u8_t variable_name[LWIP_MEM_ALIGN_BUFFER(size)]
由此可得内存池中的数组定义
u8_t memp_memory_name_base[LWIP_MEM_ALIGN_BUFFER(((num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size))))]数组名称memp_meory_name_base,数组有LWIP_MEM_ALIGN_BUFFER(((num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size))))字节,再根据宏定义可得数组大小的计算公式。假如用存储器管理获取数组存储单元的话可以生成各个数组的大小而不生成数组本身。这个时候的宏定义应该为
u32 MEMP_ArraySize[MEMP_MAX]={
#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEM_ALIGN_BUFFER(((num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size)))),
#include #include "lwip/priv/memp_std.h"
};
2)状态宏命令分析
本工程状态宏命令为LWIP_MEMPOOL_DECLARE_STATS_INSTANCE(memp_stats_ ## name),根据如下宏定义展开
#define LWIP_MEMPOOL_DECLARE_STATS_INSTANCE(name)
由此可见本工程状态空,即无状态信息。
3)memp链表宏命令分析
memp链表宏命令static struct memp *memp_tab_ ## name,由此可得各个数组的链表定义,即
static struct memp *memp_tab_name;
上述中链表名称memp_tab_name中的字符name由对应控制块的名称确定,有什么控制块就有什么控制块名称,有多少控制块就有多少各链表名称。
4)memp_desc宏命令分析
由memp_desc如下宏命令
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 \
};
根据本工程的DECLARE_LWIP_MEMPOOL_DESC如下宏定义可知本工程的数组描述字符为空。
#define DECLARE_LWIP_MEMPOOL_DESC(desc)
根据本工程的LWIP_MEMPOOL_DECLARE_STATS_REFERENCE如下宏定义可知本工程的数组状态参考项为空。
#define LWIP_MEMPOOL_DECLARE_STATS_REFERENCE(name)
根据本工程的LWIP_MEM_ALIGN_SIZE(size)如下宏定义可计算本数组所分的各块的字节数。
#define LWIP_MEM_ALIGN_SIZE(size) (((size) + MEM_ALIGNMENT - 1U) & ~(MEM_ALIGNMENT-1U))
根据本宏命令memp_memory_ ## name ## _base可知数组描述符基地址即为定义的数组地址memp_memory_name_base。
根据宏命令&memp_tab_ ## name可知数组描述的链表名称&memp_tab_name。
综上所述,存储池的描述符和数组的宏定义使得lwIP配置文件lwipopts.h中的每一个宏命令LWIP_MEMPOOL(name,num,size,desc)最后都对应变为
u8_t memp_memory_name_base[LWIP_MEM_ALIGN_BUFFER(((num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size))))]
static struct memp *memp_tab_name;
const struct memp_desc memp_ name = {
LWIP_MEM_ALIGN_SIZE(size),
(num),
memp_memory_ name _base,
&memp_tab_ ## name
};
memp.c文件中的各函数不是直接操作存储池中各数组描述变量,其操作的是存储池中的各数组描述符指针数组,因为经过这些宏定义操作后所有信息全部集中到这个数组中了。
三、lwIP存储池动态申请设计
使用平台存储管理函数为lwIP协议栈的存储池申请存储器就变成构造存储池描述符指针数组。为此要申请各个数组的存储单元、数组存储池描述符和数组的链表指针。申请数组单元要知道各个数组的字节数,申请好数组描述符后要给数组描述符填写块的大小、块的个数、数组首地址和数组链表。为此对官方memp.c进行如下改动即可实现动态申请lwIP协议的存储池,即
//#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEMPOOL_DECLARE(name,num,size,desc)
//#include "lwip/priv/memp_std.h"
//const struct memp_desc *const memp_pools[MEMP_MAX] = {
//#define LWIP_MEMPOOL(name,num,size,desc) &memp_ ## name,
//#include "lwip/priv/memp_std.h"
//};
//const struct memp_desc *const memp_pools[MEMP_MAX];
以上把memp.c中的宏定义注释丢,改成如下代码即可
struct memp_desc *memp_pools[MEMP_MAX];//改变成非常量指针数组
/******************************************************
成功执行返回0,失败返回非0
*******************************************************/
#include "stdbool.h"
bool_t lwip_memp_Get(void)
{
#include "malloc.h"
bool bErr=false;
int i=0,j=0;
//存储池用各数组大小定义
u8 *ptrBuf;
struct memp_desc *ptrMempDesc;
struct memp *ptrMemptab;;
u16_t ArraySize[MEMP_MAX]={
#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEM_ALIGN_BUFFER(((num)*(MEMP_SIZE+MEMP_ALIGN_SIZE(size)))),
#include "lwip/priv/memp_std.h"
};
//存储池用各数组链表定义
//存储池中数组块数数组
u16_t BlockNum[MEMP_MAX]={
#define LWIP_MEMPOOL(name,num,size,desc) num,
#include "lwip/priv/memp_std.h"
};
//存储池中数组中各块的大小数组定义
u16_t BlockSize[MEMP_MAX]={
#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEM_ALIGN_SIZE(size),
#include "lwip/priv/memp_std.h"
};
bErr=false;
for(i=0;i<MEMP_MAX;i++)
{
ptrMemptab=mymallocEx(sizeof(struct memp));
ptrBuf=mymallocEx(ArraySize[i]);
ptrMempDesc=mymallocEx(sizeof(struct memp_desc));
if(ptrMemptab==NULL||ptrBuf==NULL||ptrMempDesc==NULL)
{
myfreeEx(ptrMemptab);
myfreeEx(ptrBuf);
myfreeEx(ptrMempDesc);
bErr=true;
break;
}
ptrMempDesc->base=ptrBuf;
ptrMempDesc->tab=&ptrMemptab;
ptrMempDesc->num=BlockNum[i];
ptrMempDesc->size=BlockSize[i];
memp_pools[i]=ptrMempDesc;
}
if(bErr)
{
for(j=0;j<i;j++)
{
ptrMempDesc=memp_pools[i];
myfreeEx(ptrMempDesc->base);
myfreeEx((*ptrMempDesc->tab));
myfreeEx(ptrMempDesc);
memp_pools[i]=NULL;
}
}
return bErr;
}
使用此方法时必须存储池初始化之前调用该函数。上面代码中有一处有错,应把链表改成静态的链表指针数组,然后其初始化描述符中的tab成员。