内存池memp.c文件学习"/>
LWIP内存池memp.c文件学习
公众号:
声明:个人所写所有博客均为自己在学习中的记录与感想,或为在学习中总结他人学习成果,但因本人才疏学浅,如果大家在阅读过程中发现错误,欢迎大家指正。
使用LWIP源码版本为1.4.1
使用内存池分配内存的优点在于速度快、效率高、不会产生很多内存碎片,但是缺点在于只能分配各种固定大小的内存空间,LWIP必须实现知道用户要使用哪些类型的POOL,每种类型的POOL数量,然后根据这个需求建立内存池。
一、内存池管理关键函数
内存池初始化函数memp_init,在内核初始化时,该函数必须被调用,用来完成内存池的建立;
内存池分配函数memp_malloc,通常被内核调用,以实现核中固定数据结构的申请;
内存池释放函数memp_free;
二、临界区与临界资源
个人一直是跟老师自学嵌入式,许多问题也是第一次遇到,比如这个临界区。下面一些代码多次遇到了临界问题。
虽然多个进程可以共享系统中的各种资源,但其中许多资源一次只能为一个进程所使用,我们把一次仅允许一个进程使用的资源称为临界资源。许多物理设备都属于临界资源,比如打印机,输入机等。此外,还有许多变量、数据等都可以被若干进程共享,也属于临界资源。
对临界资源的访问,必须互斥的进行,在每个进程中,访问临界资源的那段代码称为临界区。为了保证临界资源的正确使用,可以把临界资源的访问过程分为四个部分:
- 进入临界区:为了进入临界区使用临界资源,在进入区要检查可否进入临界区,如果可以进入,则应设置正在访问临界区的标志,以组织其他进程同时进入临界区
- 临界区:进程中访问临界资源的那段代码,又称为临界段。
- 退出区:将正在访问临界区的标志清除
- 剩余区:代码中的其余部分
三、 memp结构体
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中已经配置了默认值。
4、memp_desc[]:
全局型指针数组,指向每类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中的每个元素指向了一个字符串,这些字符串在统计信息输出中
可能用到。
5、memp_memory[]
这是一个数组,这才是真正的内存池,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_init()
内存池的初始化,主要是为每种内存池建立链表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_POOLSmemp = (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_POOLSmemp = (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_CHECKmemp_overflow_init();/* check everything a first time to see if it worked */memp_overflow_check_all();
#endif /* MEMP_OVERFLOW_CHECK */
}
系统初始化时,函数,memp_init是必须被调用的,否则内存池空间将无效。
下图是初始化内存池空间示意图
六、内存池分配函数memp_malloc()
分配过程是如果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 >= 2memp_overflow_check_all();
#endif /* MEMP_OVERFLOW_CHECK >= 2 */memp = memp_tab[type]; //获得对应头指针指向的POOLif (memp != NULL) { //不为空,则说明还有空闲的POOLmemp_tab[type] = memp->next; //头指针指向下一个节点
#if MEMP_OVERFLOW_CHECKmemp->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; //返回可用空间起始地址
}
七、内存池释放函数memp_free()
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 >= 2memp_overflow_check_all();
#elsememp_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_CHECKLWIP_ASSERT("memp sanity", memp_sanity());
#endif /* MEMP_SANITY_CHECK */SYS_ARCH_UNPROTECT(old_level); //退出临界区
}
更多推荐
LWIP内存池memp.c文件学习
发布评论