打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
LWIP内存池memp.c文件学习

http://blog.csdn.net/Angel_94/article/details/50817918

2016

  

使用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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

四、一些重要数组

1、memp_tab:

全局指针数组,指向每类POOL的第一个POOL,memp.c文件中

static struct memp *memp_tab[MEMP_MAX];
  • 1
  • 1

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"};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

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)),    …….};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

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"};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

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    ……};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

上面的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"};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

memp_desc的真实面目如下:

static const char *memp_desc[MEMP_MAX] ={    ("RAW_PCB"),    ("UDP_PCB"),    ("TCP_PCB"),    ("TCP_PCB_LISTEN"),    ("TCP_PCB_LISTEN"),    …….};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

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"];
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

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)) ))……..];
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

其中MEMP_SIZE表示需要在每个POOL头部预留的空间,LWIP中在某些特殊
场合使用该空间中的值来对POOL进行特殊处理,这里不使用该项功能,所以
MEMP_SIZE为0,。如果使用到MEMP_SIZE的话也需要对这个大小进行内存对
齐!


五、内存池初始化函数memp_init()

内存池的初始化,主要是为每种内存池建立链表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 */}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

系统初始化时,函数,memp_init是必须被调用的,否则内存池空间将无效。

下图是初始化内存池空间示意图


六、内存池分配函数memp_malloc()

分配过程是如果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;  //返回可用空间起始地址}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

七、内存池释放函数memp_free()

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);  //退出临界区}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
lwip之内存管理
lwip
说说楼宇安防视频监控系统的设计与优化
嵌入式以太网:Lwip内存管理之内存堆
LwIP在stm32上的无操作系统移植
如何使用STM32CubeMX配置ETH(RMII)
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服