LWIP一句话记住就行:
一项工程,两份配置,三种内存分配,四套操作API,五步初始化,六个"数据流",七个数据结构
-------------------------------------------
在第一讲中我们已经简单的提到了lwip支持的三种内存分配策略,这里我们先来聊聊这三种内存分配的异同.
|C库自带内存分配策略(malloc/free/realloc)
LWIP内存分配机制 |动态内存堆(Heap)分配策略
|动态内存池(pool)分配策略
其实关于内存分配的介绍真的很多,我这里就简单谈谈我的理解吧!
我是用养鱼的经验来理解的:
按照每条鱼/平,方便阐述,哈哈!
首先是内存堆:作为一个渔夫,一开始只打算养一种鱼,姑且就是草鱼吧.像我老家就是直接整一个大水库1000平(一大片内存,一个堆),但是突然有一天我又想多样化一点儿,打算养点儿花鲢(开始有内存申请需求),直接用渔网隔离一块出来,一天突然又想养点儿鲤鱼,这次呢比花鲢多一些,那就用砖再来隔离一块出来吧...就这样到后面种类越来越多,数量呢参差不齐(前期分配要多少给多少,当然有个对齐限制).总之就是这儿一块,那儿一块,整个水库看上去就是千疮百孔,当我突然有一天想再来养龙虾致富,却发现没有地方了(申请的内存无法分配),或者说一些边边角角的又都凑不到一起(碎片太多)
接着是内存池:有了前面无法养龙虾致富的教训后,这一次我来了大改造升级,还是这一个水库,但是为了后面方便管理,我前期就提前把整个水库分成了整整齐齐的几大块(划分池子),300平的整1个,200平的整1个,100平的整3个,50平的整4个(固定每个池子大小,每种大小的个数),为了好看,我还可以搞成了中间一个大圆,四周弄成奇形怪状的.(池子的类型也不同),接下来我开始养殖致富了.上半年养250的草鱼,尴尬,200的太小,那就用300吧(同样也有碎片),但是我灵机一动,采用了一个200,一个50(没有碎片了,但是说明当一块不够时就得用两块),接着养...但是总得就1000,用着用着后面还是不够啊(也存在内存分配失败)突然有一天草鱼丰收了,买了,好个250终于可以清理一下再用来养点儿其他的了(释放).
以上了就是我的理解,可能有点儿牵强,但是对于内存堆和内存池的差异和优缺点还是很明显的.具体比较官方的大家就自己百度吧.
好了有了前面的瞎几把 介绍,相信大家已经理解了内存堆和内存池的区别了.我们接着就谈谈LWIP的玩法.
不失一般性,我呢总结几点:
1.关于API
xxx_malloc()
xxx_free()
记住这个就够了,至于xxx一般都是可以根据字面意思理解是内存堆还是内存池.
2.memp_std.h
重点来了,这个头文件理解清楚了,那么池子方式的就基本OK了.
截取部分代码
还记得上面我们养鱼的经验吗?提前划分池子,确立四个参数:
类型 个数 每个池子的大小 描述
最最关键的来了,一个宏定义展成多个数据结构.
/** Create the list of all memory pools managed by memp. MEMP_MAX represents a NULL pool at the end */
typedef enum {
#define LWIP_MEMPOOL(name,num,size,desc) MEMP_##name,
#include "lwip/priv/memp_std.h"
MEMP_MAX
} memp_t;
简单的理解上面这段代码就是把lwip/priv/memp_std.h文件中的每一项类似:
LWIP_MEMPOOL(name, num, size, desc)都给我替换成 MEMP_##name
于是就变成了这样的:
typedef enum{
MEMP_RAW_PCB,
MEMP_UDP_PCB,
MEMP_TCP_PCB,
...
MEMP_MAX,
}memp_t
同理就产生了memp_size, memp_num, memp_desc
总之内存池问自己三个问题:池子是什么类型?每种类型的池子有多少个?该种类型每个池子的大小是多少?
3.memp_memory
#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEMPOOL_DECLARE(name,num,size,desc)
#include "lwip/priv/memp_std.h"
#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 \
};
上述代码截取:memp.h
主要理解一下memp_memory,顾名思义就是指整个池子的大小.从宏定义看跟第二点提到的展开一样的处理,这里不再赘述.
那按照求总和的方式无外乎就是
小学生:sum=Sa+Sb+Sc+Sd+…
中学生:sum=aNa+bNb+cNc+dNd+…
程序员:sum=type*(size + aligned)
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)))
+((MEMP_NUM_TCP_PCB)*MEMP_SIZE+MEMP_ALIGN_SIZE(sizeof(struct tcp_pcb)))
...
}
说白了还不是中学生算法,只是程序员牛逼的引入了一个对齐的新概念.
4.对齐
提到了对齐那就简单讲一下吧.
#define LWIP_MEM_ALIGN_SIZE(size) (((size) + MEM_ALIGNMENT - 1U) & ~(MEM_ALIGNMENT-1U))
我的理解:
对齐方式:2^n(2的n次方)
请求内存:size
置0因子:末n位(& ~(MEM_ALIGNMENT-1U))的作用)
被置0因子:((size) + MEM_ALIGNMENT - 1U)二进制的后n位
举个例子:指定4字节对齐,即2^2
请求3byte
置0因子:末2未置0
被置0因子:3+4-1=6,二进制:0110
实际分配:二进制:0100即4byte
请求4byte
置0因子:末2未置0
被置0因子:4+4-1=7,二进制:0111
实际分配:二进制:0100即4byte
请求5byte
置0因子:末2未置0
被置0因子:5+4-1=8,二进制:1000
实际分配:二进制:1000即8byte
...
https://blog.csdn.net/XieWinter/article/details/99676523
https://blog.csdn.net/ZCShouCSDN/article/details/80282907
还是看看上面的两个链接吧.
最后说一下,内存这个大话题,需要讲的太多太多,但是本人目前学识有限,也还在努力学习中,大家一起加油吧.毕竟搞懂这个,感觉一切都变得简单了.
-------------------------------------------
这期就到这里了,LWIP想怎么玩就怎么玩,我们下期再见.