http://blog.csdn.net/Angel_94/article/details/50817918
2016
使用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 */}
全局指针数组,指向每类POOL的第一个POOL,memp.c文件中
static struct memp *memp_tab[MEMP_MAX];
全局数组,用来记录每个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的大小,这里的大小都是进行了内存对
齐的。
全局数组,用来记录每类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作为节点,以单链表的形式串联起来
voidmemp_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_CHECKmemp_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; //返回可用空间起始地址}
voidmemp_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); //退出临界区}
联系客服