LwIP 之 详解动态内存(内存堆、内存池)管理 二(mem.c/h、memp.c/h)

  该文主要是接上一部分LwIP 之 详解动态内存(内存堆、内存池)管理(mem.c/h、memp.c/h),该部分许多内容需要用到上一篇的内容。该部分主要是详细介绍LwIP中的动态内存池。整个内存池的实现相较于内存堆来说,还是麻烦点的。光是看源码中那一堆宏定义就够费劲!此外还有一点就是,该部分的文件结构也是挺有意思的!
  LwIP的内存池实现对应的源码文件主要就是memp.c/h。除此之外,还有两个头文件memp_prive.hmemp_std.h
之前也说过,LwIP内存池的实现受制于两个宏值MEMP_MEM_MALLOCMEM_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中定义枚举值。因此,这个文件的使用位置比较特殊,如下图
priv
重点注意上图中,高亮部分的注释。下面详细分析以下该文件。
  该文件的第一部分为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_MALLOCMEM_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"

这里就是定义所有内存池了!

未完待续…

猜你喜欢

转载自blog.csdn.net/ZCShouCSDN/article/details/80327132
今日推荐