LWIP内存池memp.c文件学习

公众号:
LWIP内存池memp.c文件学习_第1张图片
声明:个人所写所有博客均为自己在学习中的记录与感想,或为在学习中总结他人学习成果,但因本人才疏学浅,如果大家在阅读过程中发现错误,欢迎大家指正。

使用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_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是必须被调用的,否则内存池空间将无效。

下图是初始化内存池空间示意图
LWIP内存池memp.c文件学习_第2张图片


六、内存池分配函数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 >= 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;  //返回可用空间起始地址
}

七、内存池释放函数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 >= 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);  //退出临界区
}

你可能感兴趣的:(LwIP)