公众号:
声明:个人所写所有博客均为自己在学习中的记录与感想,或为在学习中总结他人学习成果,但因本人才疏学浅,如果大家在阅读过程中发现错误,欢迎大家指正。
使用LWIP源码版本为1.4.1
使用内存池分配内存的优点在于速度快、效率高、不会产生很多内存碎片,但是缺点在于只能分配各种固定大小的内存空间,LWIP必须实现知道用户要使用哪些类型的POOL,每种类型的POOL数量,然后根据这个需求建立内存池。
内存池初始化函数memp_init,在内核初始化时,该函数必须被调用,用来完成内存池的建立;
内存池分配函数memp_malloc,通常被内核调用,以实现核中固定数据结构的申请;
内存池释放函数memp_free;
个人一直是跟老师自学嵌入式,许多问题也是第一次遇到,比如这个临界区。下面一些代码多次遇到了临界问题。
虽然多个进程可以共享系统中的各种资源,但其中许多资源一次只能为一个进程所使用,我们把一次仅允许一个进程使用的资源称为临界资源。许多物理设备都属于临界资源,比如打印机,输入机等。此外,还有许多变量、数据等都可以被若干进程共享,也属于临界资源。
对临界资源的访问,必须互斥的进行,在每个进程中,访问临界资源的那段代码称为临界区。为了保证临界资源的正确使用,可以把临界资源的访问过程分为四个部分:
struct memp {
struct memp *next; //下一个链表
#if MEMP_OVERLOW_CHECK
/*发生溢出时调用函数的文件名,mem_malloc调用者的文件*/
const char *file;
/*发生溢出时调用函数的行号,mem_malloc调用者的行数*/
int line;
#endif /* MEMP_OVERFLOW_CHECK */
}
###1、memp_tab:
全局指针数组,指向每类POOL的第一个POOL,memp.c文件中
static struct memp *memp_tab[MEMP_MAX];
###2、memp_sizes:
全局数组,用来记录每个POOL的大小,memp.c文件中
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_sizes的真实面目如下:
const u16_t memp_sizes[MEMP_MAX] =
{
LWIP_MEM_ALIGN_SIZE(sizeof(struct raw_pcb)),
LWIP_MEM_ALIGN_SIZE(sizeof(struct udp_pcb)),
LWIP_MEM_ALIGN_SIZE(sizeof(struct tcp_pcb)),
LWIP_MEM_ALIGN_SIZE(sizeof(struct tcp_pcb_listen)),
LWIP_MEM_ALIGN_SIZE(sizeof(struct tcp_seg)),
…….
};
memp_sizes中保存了每种类型POOL的大小,这里的大小都是进行了内存对
齐的。
###3、memp_num[]:
全局数组,用来记录每类POOL中POOL的个数,memp.c文件中
const u16_t memp_num[MEMP_MAX]=
{
#define LWIP_MEMPOOL(name, num, size, desc) (num),
#include "lwip/memp_std.h"
};
memp_num的真实面目如下:
const u16_t memp_num[MEMP_MAX] =
{
MEMP_NUM_RAW_PCB,
MEMP_NUM_UDP_PCB,
MEMP_NUM_TCP_PCB,
MEMP_NUM_TCP_PCB_LISTEN,
MEMP_NUM_TCP_SEG
……
};
上面的MEMP_NUM_RAW_PCB、MEMP_NUM_UDP_PCB等等都是又用户
定义的,用来记录对应的POOL的数量,用户可以在lwipopts.h文件中定义,
LWIP在opt.h中已经配置了默认值。
全局型指针数组,指向每类POOL的描述符,memp.c文件中:
static sonst char *memp_desc[MEMP_MAX]=
{
#define LWIP_MEMPOOL(name, num, size, desc) (desc),
#include "lwip/memp_std.h"
};
memp_desc的真实面目如下:
static const char *memp_desc[MEMP_MAX] =
{
("RAW_PCB"),
("UDP_PCB"),
("TCP_PCB"),
("TCP_PCB_LISTEN"),
("TCP_PCB_LISTEN"),
…….
};
memp_desc中的每个元素指向了一个字符串,这些字符串在统计信息输出中
可能用到。
这是一个数组,这才是真正的内存池,memp.c文件中
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"
];
memp_memory的真实面目如下:
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)) ))
……..
];
其中MEMP_SIZE表示需要在每个POOL头部预留的空间,LWIP中在某些特殊
场合使用该空间中的值来对POOL进行特殊处理,这里不使用该项功能,所以
MEMP_SIZE为0,。如果使用到MEMP_SIZE的话也需要对这个大小进行内存对
齐!
内存池的初始化,主要是为每种内存池建立链表memp_tab,其链表是逆序的,此外,如果有统计功能使能的话,也记录了各种内存池的数目
通过struct memp作为节点,以单链表的形式串联起来
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) { //依次对每种类型的POOL进行操作
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 */
for (j = 0; j < memp_num[i]; ++j) { //将该类所有POOL组成链表的形式
memp->next = memp_tab[i];
memp_tab[i] = memp;
memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + memp_sizes[i]
#if MEMP_OVERFLOW_CHECK
+ MEMP_SANITY_REGION_AFTER_ALIGNED
#endif
); //将每个POOL的起始处转换为memp类型,以实现链表连接
}
}
#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_init是必须被调用的,否则内存池空间将无效。
分配过程是如果memp_tab[]数组中相应链表的指针为空,说明该类型的POOL已经没有了,分配失败;否则选择链表中的第一个POOL,并在POOL最开始处预留出MEMP_SIZE(这里为0)的空间,最后将有效地址返回给函数调用者。
void *
#if !MEMP_OVERFLOW_CHECK
memp_malloc(memp_t type)
#else
/* memp_t type 输入参数为需要分配的POOL的类型*/
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]; //获得对应头指针指向的POOL
if (memp != NULL) { //不为空,则说明还有空闲的POOL
memp_tab[type] = memp->next; //头指针指向下一个节点
#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);//留出预留空间,这里MEMP_SIZE为0,不预留任何空间
} 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; //返回可用空间起始地址
}
void
memp_free(memp_t type, void *mem)//两个参数,释放的POOL的类型及起始地址
{
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);//得到POOL的起始地址
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]; //将POOL插入到memp_tab[头部]
memp_tab[type] = memp;
#if MEMP_SANITY_CHECK
LWIP_ASSERT("memp sanity", memp_sanity());
#endif /* MEMP_SANITY_CHECK */
SYS_ARCH_UNPROTECT(old_level); //退出临界区
}