LWIP再探----內存池管理


這這里是接上一篇內存池管理部分的,這里如果讀者一打開memp.c的話會感覺特別那一理解原作者在干嘛,但是看懂了就明白原作者是怎么巧妙的使用了宏。廢話不多說先說了下我分析是一下宏的條件是

前提條件
MEMP_STATS = 0
MEMP_OVERFLOW_CHECK = 0

首先要去簡單的看下#include "lwip/priv/memp_std.h"文件的格式,只需要明白這個文件依賴LWIP_MEMPOOL(name,num,size,desc)這個宏,並且在文件結尾將宏清除。

因此出現底下的最難的兩塊

#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEMPOOL_DECLARE(name,num,size,desc)
#include "lwip/priv/memp_std.h"

const struct memp_desc *const memp_pools[MEMP_MAX] = {
#define LWIP_MEMPOOL(name,num,size,desc) &memp_ ## name,
#include "lwip/priv/memp_std.h"
};

先說第一個,繼續追LWIP_MEMPOOL_DECLARE的定義如下,看完繼續懵逼中。。。,但是不能慌一個個宏替換出來

#define LWIP_MEMPOOL_DECLARE(name,num,size,desc) \
  LWIP_DECLARE_MEMORY_ALIGNED(memp_memory_ ## name ## _base, ((num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size)))); \
    \
  LWIP_MEMPOOL_DECLARE_STATS_INSTANCE(memp_stats_ ## name) \
    \
  static struct memp *memp_tab_ ## name; \
    \
  const struct memp_desc memp_ ## name = { \
    DECLARE_LWIP_MEMPOOL_DESC(desc) \
    LWIP_MEMPOOL_DECLARE_STATS_REFERENCE(memp_stats_ ## name) \
    LWIP_MEM_ALIGN_SIZE(size), \
    (num), \
    memp_memory_ ## name ## _base, \
    &memp_tab_ ## name \
  };

里面相關宏的實現匯總如下

#ifndef LWIP_DECLARE_MEMORY_ALIGNED
#define LWIP_DECLARE_MEMORY_ALIGNED(variable_name, size) u8_t variable_name[LWIP_MEM_ALIGN_BUFFER(size)]     
#endif

#define LWIP_MEMPOOL_DECLARE_STATS_INSTANCE(name)      

#define DECLARE_LWIP_MEMPOOL_DESC(desc)

#define LWIP_MEMPOOL_DECLARE_STATS_REFERENCE(name)

#define LWIP_MEM_ALIGN_SIZE(size) (((size) + MEM_ALIGNMENT - 1U) & ~(MEM_ALIGNMENT-1U))

最后就有這樣一個過程

#define LWIP_MEMPOOL_DECLARE(name,num,size,desc) \
  LWIP_DECLARE_MEMORY_ALIGNED(memp_memory_ ## name ## _base, ((num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size)))); \
    \
  LWIP_MEMPOOL_DECLARE_STATS_INSTANCE(memp_stats_ ## name) \
    \
  static struct memp *memp_tab_ ## name; \
    \
  const struct memp_desc memp_ ## name = { \
    DECLARE_LWIP_MEMPOOL_DESC(desc) \
    LWIP_MEMPOOL_DECLARE_STATS_REFERENCE(memp_stats_ ## name) \
    LWIP_MEM_ALIGN_SIZE(size), \
    (num), \
    memp_memory_ ## name ## _base, \
    &memp_tab_ ## name \
  };
  
    |
    |
   \|/
   
#define LWIP_MEMPOOL_DECLARE(name,num,size,desc) \
   
  memp_memory_RAW_PCB_base[(num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size)))]; \
  static struct memp *memp_tab_RAW_PCB; \
  const struct memp_desc memp_RAW_PCB = {\
    LWIP_MEM_ALIGN_SIZE(size), \
    (num), \
    memp_memory_RAW_PCB _base,\
    &memp_tab_ RAW_PCB\ 
  };

然后就是這樣子的宏替換,此處未全部列舉

#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEMPOOL_DECLARE(name,num,size,desc)
#include "lwip/priv/memp_std.h"
    |
    |
   \|/
  memp_memory_RAW_PCB_base[(num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size)))]; \
  static struct memp *memp_tab_RAW_PCB; 
  const struct memp_desc memp_RAW_PCB = {
    “RAW_PCB”
    LWIP_MEM_ALIGN_SIZE(size), 
    (num), 
    memp_memory_RAW_PCB _base,
    &memp_tab_ RAW_PCB 
  };
  
  memp_memory_UDP_PCB_base[(num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size)))]; \
  static struct memp *memp_tab_UDP_PCB; 
  const struct memp_desc memp_UDP_PCB = {
    “UDP_PCB”      
    LWIP_MEM_ALIGN_SIZE(size), 
    (num), 
    memp_memory_UDP_PCB _base,
    &memp_tab_UDP_PCB
  };  
.
.
.

,同理理解到這里下面繼續第二個宏就是同理結果如下

const struct memp_desc *const memp_pools[MEMP_MAX] = {
#define LWIP_MEMPOOL(name,num,size,desc) &memp_ ## name,
#include "lwip/priv/memp_std.h"
};
    |
    |
   \|/

const struct memp_desc *const memp_pools[MEMP_MAX] = {
&memp_RAW_PCB,
&memp_UDP_PCB,
.
.
.
}

注意這里的MEMP_MAX是這樣來的

typedef enum {
#define LWIP_MEMPOOL(name,num,size,desc) MEMP_##name,
#include "lwip/priv/memp_std.h"
MEMP_MAX
} memp_t;
|
|
\|/
typedef enum {
MEMP_RAW_PCB,
MEMP_UDP_PCB,
.
.
.
MEMP_MAX
} memp_t;

然后這里還還需要了解一個結構體的定義如下,

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 */
  /** 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_pools就將整個mempool的內存串到了一個結構體數組中。要注意此時每個memp_pools中的memp_desc結構體中的memp_tab_UDP_PCB還只是一個指針的指針,並未有具體的實際意義。然后memp_init會進行這一工作,去掉宏不編譯的部分
memp_init如下

void memp_init(void)
{
  u16_t i;
  /* for every pool: */
  for (i = 0; i < LWIP_ARRAYSIZE(memp_pools); i++) {
    memp_init_pool(memp_pools[i]);
  }
}

就是循環調用memp_init_pool,接着看去掉宏簡化后的memp_init_pool

void
memp_init_pool(const struct memp_desc *desc)
{
  int i;
  struct memp *memp;

  *desc->tab = NULL;
  memp = (struct memp *)LWIP_MEM_ALIGN(desc->base);

  /* create a linked list of memp elements */
  for (i = 0; i < desc->num; ++i) {
    memp->next = *desc->tab;
    *desc->tab = memp;
    memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + desc->size
  }
}

到這里所有內存池的定義和初始化已經完成了借用野火的一張圖,初始化后的pool結構如下

每一個類型的池最后由,tab將所有的空閑池串起來,組成一個內存池單向鏈表。到此最難理解的部分已經完了,接下來內存池的內存分配和釋放就是很簡單的內容了。

內存申請

void * memp_malloc(memp_t type){
  void *memp;
  // 取對應內存池的控制塊
  memp = do_memp_malloc_pool(memp_pools[type]);
  return memp;
}
//這個函數內部實際上調用了 do_memp_malloc_pool簡化后如下,
static void * do_memp_malloc_pool(const struct memp_desc *desc)
{
  struct memp *memp;
  SYS_ARCH_DECL_PROTECT(old_level);
  SYS_ARCH_PROTECT(old_level);
  memp = *desc->tab;

  if (memp != NULL) {

    *desc->tab = memp->next;

    SYS_ARCH_UNPROTECT(old_level);
    /* cast through u8_t* to get rid of alignment warnings */
    return ((u8_t *)memp + MEMP_SIZE);
  } else {
    SYS_ARCH_UNPROTECT(old_level);
  }
  return NULL;
}

因為tab是空閑pool的頭,所以內存申請直接就是返回tab指向pool就可以了。同時內存釋放就是將pool從新插入單向鏈表的操作了。具體簡化的代碼如下

內存釋放

void memp_free(memp_t type, void *mem)
{
  if (mem == NULL) {
    return;
  }
  do_memp_free_pool(memp_pools[type], mem);

}
//調用do_memp_free_pool
static void do_memp_free_pool(const struct memp_desc *desc, void *mem)
{
  struct memp *memp;
  SYS_ARCH_DECL_PROTECT(old_level);

  /* cast through void* to get rid of alignment warnings */
  memp = (struct memp *)(void *)((u8_t *)mem - MEMP_SIZE);
  SYS_ARCH_PROTECT(old_level);
  memp->next = *desc->tab;
  *desc->tab = memp;

  SYS_ARCH_UNPROTECT(old_level);

}

現在LWIP的兩種內存策略的實現方式,都已經理解過了,其中內存池的溢出檢測部分沒有說,但是已經可以幫助我們使用LWIP了,作者設計兩種內存策略是有他的設計初衷的,看了#include "lwip/priv/memp_std.h"文件就知道,內存池的出現就是為一些特殊的長度固定的數據結構設計的,他分配快速,釋放亦是,並且很定不會有內存碎片,但是這還是一種空間換時間的做法,因為內存池申請函數,支持如果當前尺寸的pool用完了,可以分配更大的池。內存堆就是用來應對大小不定的內存分配場合的,當人LWIP支持用堆實現pool也支持用pool實現堆,同時還支持用戶池,這些功能都可以通過宏簡單 的配置具體如下

MEM_LIBC_MALLOC  使用C庫

MEMP_MEM_MALLOC  使用內存堆替換內襯池。

MEM_USE_POOLS  使用內存池替換內存堆

MEMP_USE_CUSTOM_POOLS   使用用戶定義的內存池,這個實現需要用戶提供一個文件lwippools.h,並按如下形式定義字節的內存池,要求內存池的大小要依次增大。

LWIP_MALLOC_MEMPOOL_START

LWIP_MALLOC_MEMPOOL(20, 256)

LWIP_MALLOC_MEMPOOL(10, 512)

LWIP_MALLOC_MEMPOOL(5, 1512)

LWIP_MALLOC_MEMPOOL_END

好了,到此LWIP的內存管理部分算是簡單的學習了一下了,內存管理完。

2019-06-16 17:58:42

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM