本章分析动态内存堆(HEAP)的第一种分配策略,承接上篇文章
LwIP 协议栈源码分析(读书笔记)–内存管理–机制策略分析
LwIP 协议栈源码分析(读书笔记)–内存管理–动态内存堆(HEAP)分配策略源码
组织结构:
源码解析:
见wlip memp.c文件。
必要的结构解释:
内存池大小=单个内存类型1大小数目1+单个内存类型2大小数目2+……+单个内存类型MEMP_MAX*数目MEMP_MAX,
内存池元素个数=数目1+数目MEMP_MAX;
/** This array holds the first free element of each pool.
* Elements form a linked list.==该数组保存一张链表,节点数=MEMP_MAX种类型的内存类型*对应的数目,即内存池元素个数 */
static struct memp *memp_tab[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"
};
/** 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"
};
memp_init():
/**
* Initialize this module.
*
* Carves out memp_memory into linked lists for each pool-type.
*/
void
memp_init(void)
{
struct memp *memp;
u16_t i, j;
for (i = 0; i < MEMP_MAX; ++i) {
MEMP_STATS_AVAIL(used, i, 0);
MEMP_STATS_AVAIL(max, i, 0);
MEMP_STATS_AVAIL(err, i, 0);
MEMP_STATS_AVAIL(avail, i, memp_num[i]);
}
#if !MEMP_SEPARATE_POOLS
memp = (struct memp *)LWIP_MEM_ALIGN(memp_memory);//内存地址对齐
#endif /* !MEMP_SEPARATE_POOLS */
/* for every pool: */
for (i = 0; i < MEMP_MAX; ++i) {//创建链表:节点数=MEMP_MAX种类型的内存类型*对应的数目
memp_tab[i] = NULL;
#if MEMP_SEPARATE_POOLS
memp = (struct memp*)memp_bases[i];
#endif /* MEMP_SEPARATE_POOLS */
/* create a linked list of memp elements==创建memp链表==将memp_num[i]个相同内存类型之间 建立链表连接,由后向前 */
for (j = 0; j < memp_num[i]; ++j) {
memp->next = memp_tab[i];
memp_tab[i] = memp;
memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + memp_sizes[i]//更新memp为下一个块地址
#if MEMP_OVERFLOW_CHECK
+ MEMP_SANITY_REGION_AFTER_ALIGNED
#endif
);
}
}
#if MEMP_OVERFLOW_CHECK
memp_overflow_init();
/* check everything a first time to see if it worked */
memp_overflow_check_all();
#endif /* MEMP_OVERFLOW_CHECK */
}
memp_malloc():
/**
* Get an element from a specific pool.
*
* @param type the pool to get an element from
*
* the debug version has two more parameters:
* @param file file name calling this function
* @param line number of line where this function is called
*
* @return a pointer to the allocated memory or a NULL pointer on error
*/
void *
#if !MEMP_OVERFLOW_CHECK
memp_malloc(memp_t type)
#else
memp_malloc_fn(memp_t type, const char* file, const int line)
#endif
{
struct memp *memp;
SYS_ARCH_DECL_PROTECT(old_level);
LWIP_ERROR("memp_malloc: type < MEMP_MAX", (type < MEMP_MAX), return NULL;);
SYS_ARCH_PROTECT(old_level);
#if MEMP_OVERFLOW_CHECK >= 2
memp_overflow_check_all();
#endif /* MEMP_OVERFLOW_CHECK >= 2 */
memp = memp_tab[type];//获取内存类型的首地址
if (memp != NULL) {
memp_tab[type] = memp->next;//更新memp_tab
#if MEMP_OVERFLOW_CHECK
memp->next = NULL;
memp->file = file;
memp->line = line;
#endif /* MEMP_OVERFLOW_CHECK */
MEMP_STATS_INC_USED(used, type);
LWIP_ASSERT("memp_malloc: memp properly aligned",
((mem_ptr_t)memp % MEM_ALIGNMENT) == 0);
memp = (struct memp*)(void *)((u8_t*)memp + MEMP_SIZE);//类型强转
} else {
LWIP_DEBUGF(MEMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("memp_malloc: out of memory in pool %s\n", memp_desc[type]));
MEMP_STATS_INC(err, type);
}
SYS_ARCH_UNPROTECT(old_level);
return memp;
}
memp_free():
/**
* Put an element back into its pool.
*
* @param type the pool where to put mem
* @param mem the memp element to free
*/
void
memp_free(memp_t type, void *mem)
{
struct memp *memp;
SYS_ARCH_DECL_PROTECT(old_level);
if (mem == NULL) {
return;
}
LWIP_ASSERT("memp_free: mem properly aligned",
((mem_ptr_t)mem % MEM_ALIGNMENT) == 0);
memp = (struct memp *)(void *)((u8_t*)mem - MEMP_SIZE);
SYS_ARCH_PROTECT(old_level);
#if MEMP_OVERFLOW_CHECK
#if MEMP_OVERFLOW_CHECK >= 2
memp_overflow_check_all();
#else
memp_overflow_check_element_overflow(memp, type);
memp_overflow_check_element_underflow(memp, type);
#endif /* MEMP_OVERFLOW_CHECK >= 2 */
#endif /* MEMP_OVERFLOW_CHECK */
MEMP_STATS_DEC(used, type);
memp->next = memp_tab[type]; //插入链表
memp_tab[type] = memp;
#if MEMP_SANITY_CHECK
LWIP_ASSERT("memp sanity", memp_sanity());
#endif /* MEMP_SANITY_CHECK */
SYS_ARCH_UNPROTECT(old_level);
}
问:
lwip 内存池的组织形式为链表,但链表本身的内存在哪里?看代码实现,他是在池中。如果这样,数据岂不是全乱套了?
不会乱套,每个池子最前面的4个字节用做指针;内存被分配后,这四个字节可以用来存储数据。这个时候被分配的块已经不在链表上了,所以指针不会保留,也没必要保留。
有人说,lwip内存池会浪费内存,但lwip实现的池,是已规划的很好的,按理来说,使用合理,还会浪费么?
这里的浪费的概念是比如系统保留了五个,但实际你只会用到三个。有两个预留的空间永远不会用到。