该文主要是接上一部分LwIP 之 详解动态内存(内存堆、内存池)管理(mem.c/h、memp.c/h),该部分许多内容需要用到上一篇的内容。该部分主要是详细介绍LwIP中的动态内存池。整个内存池的实现相较于内存堆来说,还是麻烦点的。光是看源码中那一堆宏定义就够费劲!此外还有一点就是,该部分的文件结构也是挺有意思的!
LwIP的内存池实现对应的源码文件主要就是memp.c/h
。除此之外,还有两个头文件memp_prive.h
和memp_std.h
。
之前也说过,LwIP内存池的实现受制于两个宏值MEMP_MEM_MALLOC
和MEM_USE_POOLS
的限制。在该部分的源码文件中,仍然到处可见这两个宏值。
memp_std.h
该文件主要定义了LwIP内部的内存池,此外还通过#include "lwippools.h"
的形式,将用户自定义的内存池包含进来。其为LwIP内部使用的文件,外部应用程序不应该使用该文件。这个文件被故意包含多次,一次使用LWIP_MEMPOOL()
的空定义来处理所有include,后续多次包含用来构建各种mem池列表。
该文件比较有意思,其中的宏值定义全部依赖于宏LWIP_MEMPOOL(name,num,size,desc)
,这样,只要外部提供的该宏值不同,则包含该文件后的源文件(通常为memp.h或memp_priv.h
)在预处理后,就会产生不一样的结果。这样,就可以通过在不同的地方多次包含该文件(前面必定提供宏值MEMPOOL
)以产生不同结果!下面来详细分析一下该文件。
该文件的第一部分其实就是定义了三个宏值(第一个由memp.h或memp_priv.h
提供),这三个宏值也就对应了LwIP所定义的三种内存池:
- MEMPOOL: 标准内存池(这个宏通常来自别的文件
memp.h或memp_priv.h
,通过包含该文件形式定义对应的东西) - MALLOC_MEMPOOL: 提供给
mem.c
中的mem_malloc
使用的内存池。前面说过,LwIP的内存堆可以用内存池来实现(依赖于MEMPOOL
)。 - PBUF_MEMPOOL: LwIP的pbuf结构使用的内存池(依赖于
MEMPOOL
)。pbuf再后面介绍!
上面的宏值为简写,没写参数部分。后面就是使用以上三个宏值,来定义不同的内存池!
该文件的第二部分部分就是定义上面定义的宏值,来具体定义各种LwIP内部使用的内存池。其他文件(memp.h或memp_priv.h
)包含该文件的地方,通过提供不同的MEMPOOL
,该部分将产生不同的结果。其中有个地方需要注意一下:
#if MEMP_USE_CUSTOM_POOLS
#include "lwippools.h"
#endif /* MEMP_USE_CUSTOM_POOLS */
通过以上代码,将用户自己定义的内存池引入进来。同时也不难看出,如果用户配置了使用内存池,就必须要定义文件lwippools.h
,,并且将自己需要定义的内存池放到该文件中。
最后,将所有宏值进行反定义,这样就可以保证多次包含时,不会出现宏的重复定义!
memp_prive.h
该文件定义了内存池的基本结构等,同样为LwIP内部使用的文件,外部应用程序不应该使用该文件。且该文件需要使用memp.h
中定义枚举值。因此,这个文件的使用位置比较特殊,如下图
重点注意上图中,高亮部分的注释。下面详细分析以下该文件。
该文件的第一部分为MEMP_OVERFLOW_CHECK
,其主要用来进行内存池的溢出检查,这里不多做说明。
接下来的第二大部分就是内存池各种结构的定义。首先第一个结构如下:
#if !MEMP_MEM_MALLOC || MEMP_OVERFLOW_CHECK
struct memp {
struct memp *next; /* 下一个链表 */
#if MEMP_OVERFLOW_CHECK
const char *file; /* 发生溢出时调用函数的文件名,mem_malloc调用者的文件 */
int line; /* 发生溢出时调用函数的行号,mem_malloc调用者的行数 */
#endif /* MEMP_OVERFLOW_CHECK */
};
#endif /* !MEMP_MEM_MALLOC || MEMP_OVERFLOW_CHECK */
LwIP的内存池是通过链表来组织的。上面的结构就是链表的节点结构了!
接下来是一个比较复杂的枚举变量的定义。其实,这里面就定义了两个枚举变量MEMP_POOL_HELPER_START 和 MEMP_POOL_HELPER_END
,后面的一堆东西就是给这两个枚举变量赋初值的。这其中就需要memp.h
中定义的枚举变量memp_t
中的值。
#if MEM_USE_POOLS && MEMP_USE_CUSTOM_POOLS
/* Use a helper type to get the start and end of the user "memory pools" for mem_malloc */
typedef enum {
/* Get the first (via:
MEMP_POOL_HELPER_START = ((u8_t) 1*MEMP_POOL_A + 0*MEMP_POOL_B + 0*MEMP_POOL_C + 0)*/
MEMP_POOL_HELPER_FIRST = ((u8_t)
#define LWIP_MEMPOOL(name,num,size,desc)
#define LWIP_MALLOC_MEMPOOL_START 1
#define LWIP_MALLOC_MEMPOOL(num, size) * MEMP_POOL_##size + 0
#define LWIP_MALLOC_MEMPOOL_END
#include "lwip/priv/memp_std.h"
) ,
/* Get the last (via:
MEMP_POOL_HELPER_END = ((u8_t) 0 + MEMP_POOL_A*0 + MEMP_POOL_B*0 + MEMP_POOL_C*1) */
MEMP_POOL_HELPER_LAST = ((u8_t)
#define LWIP_MEMPOOL(name,num,size,desc)
#define LWIP_MALLOC_MEMPOOL_START
#define LWIP_MALLOC_MEMPOOL(num, size) 0 + MEMP_POOL_##size *
#define LWIP_MALLOC_MEMPOOL_END 1
#include "lwip/priv/memp_std.h"
)
} memp_pool_helper_t;
首先说一下这个枚举变量是干啥的!之前曾经说过,LwIP的内存堆可以使用内存池来实现。那么为了实现该部分,内存池这里必须要提供必要的结构、接口来供内存堆实现时调用。其中,这个枚举结构就是定义出了内存池枚举memp_t
中的用户定义的供内存堆使用的内存池的枚举界限!将这个枚举各部分展开后,就是如下
#if MEM_USE_POOLS && MEMP_USE_CUSTOM_POOLS
/* Use a helper type to get the start and end of the user "memory pools" for mem_malloc */
typedef enum {
/* Get the first (via:
MEMP_POOL_HELPER_START = ((u8_t) 1*MEMP_POOL_A + 0*MEMP_POOL_B + 0*MEMP_POOL_C + 0)
“MEMP_POOL_##size” 这个东西,就是之前定义的枚举memp_t中 ,用户自定义的内存池的枚举值,结果一步步替换后就成该名
*/
MEMP_POOL_HELPER_FIRST = ((u8_t)
#define LWIP_MEMPOOL(name,num,size,desc)
#define LWIP_MALLOC_MEMPOOL_START 1
#define LWIP_MALLOC_MEMPOOL(num, size) * MEMP_POOL_##size + 0
#define LWIP_MALLOC_MEMPOOL_END
// #if MEMP_USE_CUSTOM_POOLS
// #include "lwippools.h"
// #endif /* MEMP_USE_CUSTOM_POOLS */
// /* 这里将其展开之后就是如下 */
LWIP_MALLOC_MEMPOOL_START
LWIP_MALLOC_MEMPOOL(20, 256) -> MEMP_POOL_256
LWIP_MALLOC_MEMPOOL(10, 512) -> MEMP_POOL_512
LWIP_MALLOC_MEMPOOL(5, 1512) -> MEMP_POOL_1512
LWIP_MALLOC_MEMPOOL_END
#undef LWIP_MEMPOOL
#undef LWIP_MALLOC_MEMPOOL
#undef LWIP_MALLOC_MEMPOOL_START
#undef LWIP_MALLOC_MEMPOOL_END
#undef LWIP_PBUF_MEMPOOL
) , /*最终 MEMP_POOL_HELPER_START = 1*MEMP_POOL_256+0*MEMP_POOL_512+0*MEMP_POOL_1512 = MEMP_POOL_256*/
/* Get the last (via:
MEMP_POOL_HELPER_END = ((u8_t) 0 + MEMP_POOL_A*0 + MEMP_POOL_B*0 + MEMP_POOL_C*1) 结束值展开和上面一样不再说明 */
MEMP_POOL_HELPER_LAST = ((u8_t)
#define LWIP_MEMPOOL(name,num,size,desc)
#define LWIP_MALLOC_MEMPOOL_START
#define LWIP_MALLOC_MEMPOOL(num, size) 0 + MEMP_POOL_##size *
#define LWIP_MALLOC_MEMPOOL_END 1
#include "lwip/priv/memp_std.h"
)/* 最终 MEMP_POOL_HELPER_LAST = 0 + MEMP_POOL_256 * 0 + MEMP_POOL_512 * 0 + MEMP_POOL_1512 * 1 = MEMP_POOL_1512 */
} memp_pool_helper_t;
最终,宏MEMP_POOL_HELPER_FIRST = MEMP_POOL_256
,而宏MEMP_POOL_HELPER_LAST = MEMP_POOL_1512
。
在接下来就是内存池描述符的结构定义了,在LwIP1.4.1版本中,以下各部分都是独立的,最新版中将其统一成了一个结构体。具体如下:
/** Memory pool descriptor */
struct memp_desc {
#if defined(LWIP_DEBUG) || MEMP_OVERFLOW_CHECK || LWIP_STATS_DISPLAY
/** Textual description */
const char *desc;
#endif /* LWIP_DEBUG || MEMP_OVERFLOW_CHECK || LWIP_STATS_DISPLAY */
#if MEMP_STATS
/** Statistics */
struct stats_mem *stats;
#endif
/** Element size */
u16_t size;
#if !MEMP_MEM_MALLOC
/** Number of elements */
u16_t num;
/** Base address */
u8_t *base;
/** First free element of each pool. Elements form a linked list. */
struct memp **tab;
#endif /* MEMP_MEM_MALLOC */
};
该文件的最后一部分就是个接口的定义了,其实现均在memp.c
文件中,后面会详细的说明。
memp.h
该文件主要就是定义以下内存池使用的宏,结构等,其中部分结构放在了memp_priv.h
文件中,如果之前看过LwIP1.4.1的源码就会知道,原来是没有memp_priv.h
文件的,所有结构都在memp.h
中,最新的源码进行结构调整。文件开始就是如下:
/* run once with empty definition to handle all custom includes in lwippools.h*/
#define LWIP_MEMPOOL(name,num,size,desc) /* 这个宏为空 */
#include "lwip/priv/memp_std.h" /* 其中的定义均是基于上面宏值得,也就成了空 */
结合memp_std.h
不难发现,将各宏展开后其实就剩下两句:
#if MEMP_USE_CUSTOM_POOLS
#include "lwippools.h"
#endif
其实,再将上面展开后,里面定义的内存池也是无效的,如果文件lwippools.h
中有引入其他文件或结构是,这几句唯一的作用就是将用户自定义的内存池时,用到的各种东西引入进来。否则以上就会全部为空,啥也不剩!
接下来是如下几句,这个枚举就是列出LwIP中定义的各种内存池的对应的枚举值。
typedef enum {
#define LWIP_MEMPOOL(name,num,size,desc) MEMP_##name, /* ## 为连接符 且会忽略##前后的空格, 例如:a ## b 其实就是 ab */
#include "lwip/priv/memp_std.h"
MEMP_MAX
} memp_t;
这个用来为每个定义的内存池定义一个枚举变量值。memp_std.h
中通过使用宏值#define LWIP_MEMPOOL(name,num,size,desc) MEMP_##name,
来定义各种枚举值,简单将宏值展开后,就是如下
typedef enum {
/*
* A list of internal pools used by LWIP.
*
* LWIP_MEMPOOL(pool_name, number_elements, element_size, pool_description)
* creates a pool name MEMP_pool_name. description is used in stats.c
*/
#if LWIP_RAW
MEMP_RAW_PCB,
#endif /* LWIP_RAW */
#if LWIP_UDP
MEMP_UDP_PCB,
#endif /* LWIP_UDP */
#if LWIP_TCP
MEMP_TCP_PCB,
MEMP_TCP_PCB_LISTEN,
MEMP_TCP_SEG,
#endif /* LWIP_TCP */
#if LWIP_IPV4 && IP_REASSEMBLY
MEMP_REASSDATA,
#endif /* LWIP_IPV4 && IP_REASSEMBLY */
#if (IP_FRAG && !LWIP_NETIF_TX_SINGLE_PBUF) || (LWIP_IPV6 && LWIP_IPV6_FRAG)
MEMP_FRAG_PBUF,
#endif /* IP_FRAG && !LWIP_NETIF_TX_SINGLE_PBUF || (LWIP_IPV6 && LWIP_IPV6_FRAG) */
#if LWIP_NETCONN || LWIP_SOCKET
MEMP_NETBUF,
MEMP_NETCONN,
#endif /* LWIP_NETCONN || LWIP_SOCKET */
#if NO_SYS==0
MEMP_TCPIP_MSG_API,
#if LWIP_MPU_COMPATIBLE
MEMP_API_MSG,
#if LWIP_DNS
MEMP_DNS_API_MSG,
#endif
#if LWIP_SOCKET && !LWIP_TCPIP_CORE_LOCKING
MEMP_SOCKET_SETGETSOCKOPT_DATA,
#endif
#if LWIP_NETIF_API
MEMP_NETIFAPI_MSG,
#endif
#endif /* LWIP_MPU_COMPATIBLE */
#if !LWIP_TCPIP_CORE_LOCKING_INPUT
MEMP_TCPIP_MSG_INPKT,
#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */
#endif /* NO_SYS==0 */
#if LWIP_IPV4 && LWIP_ARP && ARP_QUEUEING
MEMP_ARP_QUEUE,
#endif /* LWIP_IPV4 && LWIP_ARP && ARP_QUEUEING */
#if LWIP_IGMP
MEMP_IGMP_GROUP,
#endif /* LWIP_IGMP */
#if LWIP_TIMERS && !LWIP_TIMERS_CUSTOM
MEMP_SYS_TIMEOUT,
#endif /* LWIP_TIMERS && !LWIP_TIMERS_CUSTOM */
#if LWIP_DNS && LWIP_SOCKET
MEMP_NETDB,
#endif /* LWIP_DNS && LWIP_SOCKET */
#if LWIP_DNS && DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC
MEMP_LOCALHOSTLIST,
#endif /* LWIP_DNS && DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
#if LWIP_IPV6 && LWIP_ND6_QUEUEING
MEMP_ND6_QUEUE,
#endif /* LWIP_IPV6 && LWIP_ND6_QUEUEING */
#if LWIP_IPV6 && LWIP_IPV6_REASS
MEMP_IP6_REASSDATA,
#endif /* LWIP_IPV6 && LWIP_IPV6_REASS */
#if LWIP_IPV6 && LWIP_IPV6_MLD
MEMP_MLD6_GROUP,
#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
/*
* A list of pools of pbuf's used by LWIP.
*
* LWIP_PBUF_MEMPOOL(pool_name, number_elements, pbuf_payload_size, pool_description)
* creates a pool name MEMP_pool_name. description is used in stats.c
* This allocates enough space for the pbuf struct and a payload.
* (Example: pbuf_payload_size=0 allocates only size for the struct)
*/
MEMP_PBUF,
MEMP_PBUF_POOL,
/*
* Allow for user-defined pools; this must be explicitly set in lwipopts.h
* since the default is to NOT look for lwippools.h
*/
// #if MEMP_USE_CUSTOM_POOLS
// #include "lwippools.h"
// /* 这里将其展开之后就是如下 */
// LWIP_MALLOC_MEMPOOL_START
// LWIP_MALLOC_MEMPOOL(20, 256) -> MEMP_POOL_256
// LWIP_MALLOC_MEMPOOL(10, 512) -> MEMP_POOL_512
// LWIP_MALLOC_MEMPOOL(5, 1512) -> MEMP_POOL_1512
// LWIP_MALLOC_MEMPOOL_END
// #endif /* MEMP_USE_CUSTOM_POOLS */
MEMP_POOL_256,
MEMP_POOL_512,
MEMP_POOL_1512,
MEMP_MAX
} memp_t;
接下来,就是#include "lwip/priv/memp_priv.h"
,这个文件的引用必须放在这里,因为其内部会使用上面的枚举值(memp_t)。继续是一些宏定义,其中比较重要的就是LWIP_MEMPOOL_DECLARE(name,num,size,desc)
,这个宏最终用来定义出各内存池!而这其中,根据用户配置的宏值MEMP_MEM_MALLOC
和MEM_USE_POOLS
的不同,会有不同的定义。其中前者方式下比较简单、后者则相对来说复杂一些!
该文件的最后一部分就是个接口的定义了,其实现均在memp.c
文件中,后面会详细的说明。
memp.c
该文件主要就针对上面各种定义的具体实现了。文件开头是如下结构
#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEMPOOL_DECLARE(name,num,size,desc)
#include "lwip/priv/memp_std.h"
这里就是定义所有内存池了!
未完待续…