LWIP_简记(4.高大上的内存管理)

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内存分配

好了有了前面的瞎几把 介绍,相信大家已经理解了内存堆和内存池的区别了.我们接着就谈谈LWIP的玩法.

内存池

不失一般性,我呢总结几点:

1.关于API
xxx_malloc()
xxx_free()
记住这个就够了,至于xxx一般都是可以根据字面意思理解是内存堆还是内存池.

2.memp_std.h
重点来了,这个头文件理解清楚了,那么池子方式的就基本OK了.
截取部分代码
LWIP_简记(4.高大上的内存管理)_第1张图片
还记得上面我们养鱼的经验吗?提前划分池子,确立四个参数:

类型   个数   每个池子的大小   描述

最最关键的来了,一个宏定义展成多个数据结构.

/** 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想怎么玩就怎么玩,我们下期再见.

你可能感兴趣的:(#,lwip专栏,lwip,内存管理)