LwIP系列--内存管理(内存池)详解

news2025/1/13 17:07:40

一、目的

在《LwIP系列--内存管理(堆内存)详解》中我们详细介绍了LwIP中内存堆的实现原理,本篇我们介绍LwIP中内存池的实现细节。

在LwIP源码中为了满足特定内存分配的需要以及优化内存占用制定了各种尺寸大小的内存池(每种内存池管理的内存块大小固定)。

内存池分配方案的特点:

  • 每个内存块大小固定(由于根据大小从不同内存池中获取内存块,故内存堆的内存碎片得到优化)

  • 不存在内存碎片

  • 分配时间相对固定

因为LwIP需要根据TCP/IP协议各层需要不同大小的数据包,故针对不同层和协议制定了不同大小的内存池。

内存堆示意图

上图中定义了A/B/C各种尺寸的内存池,内存池A管理着A-1/A-2/A3等内存块,每个内存池可以有不同数量相同大小的内存块,不同内存池中的内存块的大小一般不相同。

二、介绍

在正式介绍内存池实现细节之前我们先理解跟内存池相关的宏定义和结构体,LwIP在内存池的实现上定义了各种宏定义

宏定义说明

LWIP_DEBUG
MEMP_OVERFLOW_CHECK
LWIP_STATS_DISPLAY
MEMP_STATS
MEMP_MEM_MALLOC

LWIP_DEBUG定义是否调试LwIP内核

MEMP_OVERFLOW_CHECK定义是否进行内存池溢出检查

LWIP_STATS_DISPLAY定义是否进行统计数据显示

MEMP_STATS定义是否打开内存池统计

MEMP_MEM_MALLOC定义内存池的分配和释放是否使用mem_malloc/mem_free(也就是不使用内存池的分配算法而使用动态内存堆方式进行分配)

#define MEMP_SIZE          (LWIP_MEM_ALIGN_SIZE(sizeof(struct memp)) + MEM_SANITY_REGION_BEFORE_ALIGNED)

内存池描述

/** 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 */
};

各字段含义说明:

desc:内存池的字符串描述(在调试、内存池溢出检查、数据统计时定义该字段)

stats:内存池统计信息,包括可用内存,最大内存、已经分配的内存信息(定义MEMP_STATS时定义该字段)

size:记录内存池中每个内存块的大小

num:记录该内存池中有几个内存块

base:内存池的首地址

tab:通过此字段将内存块构成链表进行管理(指向空闲的第一个内存块)

其中size/num/base/tab定义了内存池的几个关键字段(在MEMP_MEM_MALLOC=0时定义这些字段)

/** Memory stats */
struct stats_mem {
#if defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY
  const char *name;
#endif /* defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY */
  STAT_COUNTER err;
  mem_size_t avail;
  mem_size_t used;
  mem_size_t max;
  STAT_COUNTER illegal;
};

结构体stats_mem定义了内存的使用信息

各字段如下:

name:此结构体变量的描述性信息(用于指示属于某个内存结构)

err: 内存分配出错的次数

avail:可用内存

used:已使用内存

max:最大内存

#if !MEMP_MEM_MALLOC || MEMP_OVERFLOW_CHECK
struct memp {
  struct memp *next;
#if MEMP_OVERFLOW_CHECK
  const char *file;
  int line;
#endif /* MEMP_OVERFLOW_CHECK */
};
#endif /* !MEMP_MEM_MALLOC || MEMP_OVERFLOW_CHECK */

各个字段的含义:

next:下一个内存块的地址

file:调用内存池分配函数的文件

line:调用内存池分配函数的行数

内存池类型

在LwIP中定义了各种类型的内存池,故使用一个枚举类型来标记不同的类型的内存池

/** Create the list of all memory pools managed by memp. MEMP_MAX represents a NULL pool at the end */
typedef enum {
#define LWIP_MEMPOOL(name,num,size,desc)  MEMP_##name,
#include "lwip/priv/memp_std.h"
  MEMP_MAX
} memp_t;

注意宏定义LWIP_MEMPOOL的定义

##用于拼接字符串

上面的代码片段定义了memp_t枚举类型,MEMP_MAX定义了memp_t枚举类型的最大值。

举例说明

LWIP_MEMPOOL(MYPOOL, 4, 16, "my pool type")
//相当于定义枚举值MEMP_MYPOOL

在memp_std.h头文件中的定义了各种内存池

/*
 * 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
LWIP_MEMPOOL(RAW_PCB,        MEMP_NUM_RAW_PCB,         sizeof(struct raw_pcb),        "RAW_PCB")
#endif /* LWIP_RAW */

#if LWIP_UDP
LWIP_MEMPOOL(UDP_PCB,        MEMP_NUM_UDP_PCB,         sizeof(struct udp_pcb),        "UDP_PCB")
#endif /* LWIP_UDP */

#if LWIP_TCP
LWIP_MEMPOOL(TCP_PCB,        MEMP_NUM_TCP_PCB,         sizeof(struct tcp_pcb),        "TCP_PCB")
LWIP_MEMPOOL(TCP_PCB_LISTEN, MEMP_NUM_TCP_PCB_LISTEN,  sizeof(struct tcp_pcb_listen), "TCP_PCB_LISTEN")
LWIP_MEMPOOL(TCP_SEG,        MEMP_NUM_TCP_SEG,         sizeof(struct tcp_seg),        "TCP_SEG")
#endif /* LWIP_TCP */

#if LWIP_ALTCP && LWIP_TCP
LWIP_MEMPOOL(ALTCP_PCB,      MEMP_NUM_ALTCP_PCB,       sizeof(struct altcp_pcb),      "ALTCP_PCB")
#endif /* LWIP_ALTCP && LWIP_TCP */

上面的代码中定义了MEMP_RAW_PCB/MEMP_UDP_PCB/MEMP_TCP_PCB/MEMP_TCP_PCB_LISTEN/MEMP_TCP_SEG/MEMP_ALTCP_PCB这些枚举值,即存在这些类型的内存池

定义内存池

#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 \
  };

各个字段的含义:

name:内存池的名字

num:内存池中内存块的个数

size:内存池中每个内存块的大小

desc:内存池的描述字符串

举例说明:

LWIP_MEMPOOL_DECLARE(mypool, 4, 16, "my pool")

上面的代码片段宏展开后的代码如下:

u8_t memp_memory_mypool_base[4 * (MEMP_SIZE + 16)];
static struct stats_mem memp_stats_mypool;
static struct memp *memp_tab_mypool;
const struct memp_desc memp_mypool = {
    "my pool",
    &memp_stats_mypool,
    16,
    4,
    memp_memory_mypool_base,
    &memp_tab_mypool
};

其中MEMP_SIZE用于完整性检查,一般定义MEMP_SIZE为0即可;

在上面的代码中我们通过LWIP_MEMPOOL_DECLARE宏定义就定义了一个新的内存池mypool以及相关的内存以及描述信息。

memp_memory_mypool_base数组就是内存池需要的内存空间

内存池初始化

#define LWIP_MEMPOOL_INIT(name)    memp_init_pool(&memp_ ## name)

/**
 * Initialize custom memory pool.
 * Related functions: memp_malloc_pool, memp_free_pool
 *
 * @param desc pool to initialize
 */
void
memp_init_pool(const struct memp_desc *desc)
{
#if MEMP_MEM_MALLOC    //①
  LWIP_UNUSED_ARG(desc);
#else
  int i;
  struct memp *memp;

  *desc->tab = NULL;
  memp = (struct memp *)LWIP_MEM_ALIGN(desc->base);    //②
#if MEMP_MEM_INIT                                //③
  /* force memset on pool memory */
  memset(memp, 0, (size_t)desc->num * (MEMP_SIZE + desc->size 
#if MEMP_OVERFLOW_CHECK
                                       + MEM_SANITY_REGION_AFTER_ALIGNED
#endif
                                      ));
#endif
  /* create a linked list of memp elements */
  for (i = 0; i < desc->num; ++i) {  //④
    memp->next = *desc->tab;
    *desc->tab = memp;
#if MEMP_OVERFLOW_CHECK
    memp_overflow_init_element(memp, desc);
#endif /* MEMP_OVERFLOW_CHECK */
    /* cast through void* to get rid of alignment warnings */
    memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + desc->size
#if MEMP_OVERFLOW_CHECK
                                   + MEM_SANITY_REGION_AFTER_ALIGNED
#endif
                                  );
  }
#if MEMP_STATS                
  desc->stats->avail = desc->num;
#endif /* MEMP_STATS */
#endif /* !MEMP_MEM_MALLOC */

#if MEMP_STATS && (defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY)
  desc->stats->name  = desc->desc;
#endif /* MEMP_STATS && (defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY) */
}

①MEMP_MEM_MALLOC宏定义代表使用内存堆的分配方式作为内存池实现,此处我们讲解的是内存池的实现原理,故未定义此宏

②对栈上变量memp进行赋值

③对memp开始的地址进行清零操作

④对内存块管理结构进行赋值(将各个内存块通过next指针链接起来)

初始化后的内存布局如下:

第一个内存块的next指针为NULL,第二个内存块的next指向第一个内存块,依次类推;最终内存块的描述结构desc->tab指向内存池最后一个内存块

从内存池中分配内存

#define LWIP_MEMPOOL_ALLOC(name)   memp_malloc_pool(&memp_ ## name)

举例如果从刚才定义的mypool内存池中分配内存就可以这样操作

void *ptr = LWIP_MEMPOOL_ALLOC(mypool)
//也可以
void *ptr = memp_malloc_pool(&memp_mypool);
void *
#if !MEMP_OVERFLOW_CHECK
memp_malloc_pool(const struct memp_desc *desc)
#else
memp_malloc_pool_fn(const struct memp_desc *desc, const char *file, const int line)
#endif
{
  LWIP_ASSERT("invalid pool desc", desc != NULL);
  if (desc == NULL) {
    return NULL;
  }

#if !MEMP_OVERFLOW_CHECK
  return do_memp_malloc_pool(desc);
#else
  return do_memp_malloc_pool_fn(desc, file, line);
#endif
}
static void *
#if !MEMP_OVERFLOW_CHECK
do_memp_malloc_pool(const struct memp_desc *desc)
#else
do_memp_malloc_pool_fn(const struct memp_desc *desc, const char *file, const int line)
#endif
{
  struct memp *memp;
  SYS_ARCH_DECL_PROTECT(old_level);    //①

#if MEMP_MEM_MALLOC
  memp = (struct memp *)mem_malloc(MEMP_SIZE + MEMP_ALIGN_SIZE(desc->size));
  SYS_ARCH_PROTECT(old_level);
#else /* MEMP_MEM_MALLOC */
  SYS_ARCH_PROTECT(old_level);

  memp = *desc->tab;                //②
#endif /* MEMP_MEM_MALLOC */

  if (memp != NULL) {
#if !MEMP_MEM_MALLOC
#if MEMP_OVERFLOW_CHECK == 1
    memp_overflow_check_element(memp, desc);
#endif /* MEMP_OVERFLOW_CHECK */

    *desc->tab = memp->next;            //③
#if MEMP_OVERFLOW_CHECK
    memp->next = NULL;
#endif /* MEMP_OVERFLOW_CHECK */
#endif /* !MEMP_MEM_MALLOC */
#if MEMP_OVERFLOW_CHECK
    memp->file = file;
    memp->line = line;
#if MEMP_MEM_MALLOC
    memp_overflow_init_element(memp, desc);
#endif /* MEMP_MEM_MALLOC */
#endif /* MEMP_OVERFLOW_CHECK */
    LWIP_ASSERT("memp_malloc: memp properly aligned",
                ((mem_ptr_t)memp % MEM_ALIGNMENT) == 0);
#if MEMP_STATS
    desc->stats->used++;
    if (desc->stats->used > desc->stats->max) {
      desc->stats->max = desc->stats->used;
    }
#endif
    SYS_ARCH_UNPROTECT(old_level);
    /* cast through u8_t* to get rid of alignment warnings */
    return ((u8_t *)memp + MEMP_SIZE);  //④
  } else {
#if MEMP_STATS
    desc->stats->err++;
#endif
    SYS_ARCH_UNPROTECT(old_level);
    LWIP_DEBUGF(MEMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("memp_malloc: out of memory in pool %s\n", desc->desc));
  }

  return NULL;
}

①分配内存时先进行保护

②获取内存池第一个空闲块的地址

③将内存池空闲块的指针指向当前块的下一个,并将当前块的next设置为NULL

④将获取的空闲内存块的地址返回(此处是memp + MEMP_SIZE)

上图从内存池中分配一个内存块后的内存布局

上图是从内存池中再次分配一个内存块后的内存布局

释放内存

#define LWIP_MEMPOOL_FREE(name, x) memp_free_pool(&memp_ ## name, (x))
/**
 * Put a custom pool element back into its pool.
 *
 * @param desc the pool where to put mem
 * @param mem the memp element to free
 */
void
memp_free_pool(const struct memp_desc *desc, void *mem)
{
  LWIP_ASSERT("invalid pool desc", desc != NULL);
  if ((desc == NULL) || (mem == NULL)) {
    return;
  }

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

  LWIP_ASSERT("memp_free: mem properly aligned",
              ((mem_ptr_t)mem % MEM_ALIGNMENT) == 0);

  /* cast through void* to get rid of alignment warnings */
  memp = (struct memp *)(void *)((u8_t *)mem - MEMP_SIZE);  //②

  SYS_ARCH_PROTECT(old_level);    //③

#if MEMP_OVERFLOW_CHECK == 1
  memp_overflow_check_element(memp, desc);
#endif /* MEMP_OVERFLOW_CHECK */

#if MEMP_STATS
  desc->stats->used--;
#endif

#if MEMP_MEM_MALLOC
  LWIP_UNUSED_ARG(desc);
  SYS_ARCH_UNPROTECT(old_level);
  mem_free(memp);
#else /* MEMP_MEM_MALLOC */
  memp->next = *desc->tab;   //④
  *desc->tab = memp;  //⑤

#if MEMP_SANITY_CHECK
  LWIP_ASSERT("memp sanity", memp_sanity(desc));
#endif /* MEMP_SANITY_CHECK */

  SYS_ARCH_UNPROTECT(old_level);
#endif /* !MEMP_MEM_MALLOC */
}

①声明保护

②获取内存块的首地址

③对释放过程进行保护

④将要释放的内存块的next指向内存池中的空闲块的地址

⑤将内存池的空闲块指针指向当前块

上图中内存块D释放后的内存布局

至此,我们基本讲解完了LwIP的内存池实现(原理上还是很简单的)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/178863.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Golang应用执行Shell命令实战教程

本文学习如何在Golang程序中执行Shell命令&#xff08;如&#xff0c;ls&#xff0c;mkdir或grep&#xff09;&#xff0c;如何通过stdin和stdout传入I/O给正在运行的命令&#xff0c;同时管理长时间运行的命令。为了更好的理解&#xff0c;针对不同场景由浅入深提供几个示例进…

77、TensoRF: Tensorial Radiance Fields

简介 主页&#xff1a;https://apchenstu.github.io/TensoRF/ 总体而言&#xff0c;该文章主要内容于DVGO类似 将场景的亮度场建模为4D张量&#xff0c;它表示一个具有每体素多通道特征的3D体素网格&#xff0c;中心思想是将4D场景张量分解为多个紧凑低秩张量分量&#xff0c…

06 | 要找工作了,应该如何准备?

前言 前言&#xff1a;找工作更像相亲&#xff0c;总有一款适合自己。简历就像一份广告&#xff0c;对方要什么你写什么&#xff0c;而不是你有什么。 文章目录前言一、找工作的流程二、做法1. 分析职位描述&#xff08;JD&#xff09;1&#xff09;组成2&#xff09;做法一、找…

【数据结构】7.2 线性表的查找

7.2.1 顺序查找&#xff08;线性查找&#xff09; 应用范围&#xff1a; 顺序表或线性链表表示的静态查找表。表内元素之间可以无序。 数据元素类型定义&#xff1a; 数据表可能有多个数据域的值&#xff0c;比如成绩表中有姓名、成绩、总分等。所以用结构类型来表示要存储…

背景颜色和背景图片

<!DOCTYPE html> <html> <head> <meta charset"utf-8"> <!--这行代码是告诉浏览器需要使用"utf-8"字符集打开 因为HBuilder工具是采用utf-8编码的 注意&#xff1a;并不是设置当前页面的字符集编码方式--> …

SpringMVC(十二):SpringMVC文件下载

文章目录 SpringMVC文件下载 一、下载之前的准备 二、下载的基本流程 三、下载的请求数据 1、下载的后台实现 2、下载的示例代码 SpringMVC文件下载 一、下载之前的准备 展示所有玩家信息&#xff0c;包括图片展示。 <% page contentType"text/html;charset…

Linux_常见命令

1.ls ls -l 列出隐藏文件&#xff0c;并显示10项权限,类似如同下图 在部分发行版本的linux下&#xff0c;ll等同于ls -l 首先&#xff0c;第一列为-则代表着这一列是文件&#xff0c; 第一列为d则代表这一列为目录 除了第一位&#xff0c;那么其他还有9位&#xff0c;分为3组…

学习shell与shell script 02_vim 的保存恢复与打开时的警告信息

[1] Stopped vim test.txt $kill -9 %1[1] Stopped vim test.txt $jobs [1] Killed vim test.txt编辑一个文件后&#xff0c;使用Ctrl z 挂起。然后被不正常中断&#xff0c;导致暂存盘无法通过正常的流程来结束。所以暂…

AHB协议(2/2)

Charpter 4 Bus Interconnection 4.1 互联结构 互联结构在系统中提供了M端和S端的连接。单一的M端只需要使用一个解码器和多路选择器。 一个多M端的系统中需要使用提供仲裁和将信号从不同的M端分发到对应的S端的互联结构。分发结构需要地址&#xff0c;控制信号和写数据。 4…

ARM uboot的常用命令

一、uboot 的常用命令1 1、类似 linux 终端的行缓冲命令行 (1) 行缓冲的意思就是&#xff1a;当我们向终端命令行输入命令的时候&#xff0c;这些命令没有立即被系统识别&#xff0c;而是被缓冲到一个缓存区&#xff08;也就是系统认为我们还没有输入完&#xff09;&#xff…

2.5 变量与常量

文章目录1.变量1.1 变量的声明1.2 变量的命名1.3 变量的初始化1.4 变量的使用2 常量1.变量 和其他的编程语言一样&#xff0c;JAVA也是用变量来存储值的&#xff0c;常量可以理解为一种特殊的变量&#xff0c;其特殊在它的值是不变的。 1.1 变量的声明 声明变量本质就是创造…

自动驾驶感知——环境感知的基本概念

文章目录1. 智能汽车概述1.1 汽车新四化1.2 智能网联汽车1.3 SAE J3016自动驾驶分级标准2. 环境感知定义2.1 智能网联汽车系统架构2.2 环境感知定义及对象3. 各类传感器的介绍4. 环境感知经典算法概述5. 自动驾驶仿真软件的功能5.1 背景介绍5.2 自动驾驶仿真软件平台背后的科学…

(二十六)Set系列集合

简介: Set集合&#xff0c;基础自Collection。特征是插入无序&#xff0c;不可指定位置访问。Set集合的实现类可说是基于Map集合去写的。通过内部封装Map集合来实现的比如HashSet内部封装了HashMap。Set集合的数据库不能重复&#xff08; 或 eqauls&#xff09;的元素Set集合的…

AxMath使用教程(持续更新中)

前言 这两天学了学Latex&#xff0c;主要是为了以后写毕业论文做铺垫&#xff0c;而且Latex在数学公式这一方面&#xff0c;要比Word方便许多&#xff0c;于是我就下载了一款国产的公式编辑器——AxMath。永久会员不贵&#xff0c;只要36元&#xff0c;而且软件很好用&#xf…

【自然语言处理】情感分析(一):基于 NLTK 的 Naive Bayes 实现

情感分析&#xff08;一&#xff09;&#xff1a;基于 NLTK 的 Naive Bayes 实现朴素贝叶斯&#xff08;Naive Bayes&#xff09;分类器可以用来确定输入文本属于某一组类别的概率。例如&#xff0c;预测评论是正面的还是负面的。 它是 “朴素的”&#xff0c;它假设文本中的单…

机器学习: Label vs. One Hot Encoder

如果您是机器学习的新手&#xff0c;您可能会对这两者感到困惑——Label 编码器和 One-Hot 编码器。这两个编码器是 Python 中 SciKit Learn 库的一部分&#xff0c;它们用于将分类数据或文本数据转换为数字&#xff0c;我们的预测模型可以更好地理解这些数字。今天&#xff0c…

图机器学习-节点嵌入(Node Embedding)

图机器学习-节点嵌入(Node Embedding) Node Embedding简单点来说就是将一个node表示为一个RdR^dRd的向量。 EncoderDecoder Framework 我们首先需要设计一个encoder对节点进行编码。既然要比较相似度那么我就需要定义节点的相似度。同时我们还需要定义一个decoder&#xff0…

Java线程的生命周期的五种状态

在java中&#xff0c;任何对象都要有生命周期&#xff0c;线程也不例外&#xff0c;它也有自己的生命周期。当Thread对象创建完成时&#xff0c;线程的生命周期便开始了&#xff0c;当run()方法中代码正常执行完毕或者线程抛出一个未捕获的异常(Exception)或者错误(Error)时&am…

通信原理简明教程 | 数字基带传输

文章目录1数字基带传输系统的基本组成2 数字基带信号及其频域特性2.1 基本码型2.2 常用码型2.3 数字基带信号的功率谱3 码间干扰3.1 码间干扰的概念&#xff08;ISI&#xff09;3.2 无码间干扰传输的条件3.3 无码间干扰的典型传输波形4 部分响应和均衡技术&#xff08;*&#x…

java+ssm网上书店图书销售评价系统

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 2 第3章 系统分析 4 3.1 需求分析 4 3.2 系统可行性分析 4 3.2.1技术可行性&#xff1a;技术背景 4 3.2.2经…