内存堆其实就是一个数组。为了方便管理需要将内存堆首尾组织成内存块,因此多分配的2 * SIZEOF_STRUCT_MEM大小的空间

/* 内存堆空间 */
static u8_t ram_heap[MEM_SIZE_ALIGNED + (2 * SIZEOF_STRUCT_MEM) + MEM_ALIGNMENT];

动态内存堆会被组织成内存块,用于管理内存的分配和释放

/* 内存块结构体 */
struct mem {/* 指向后一个内存块 */mem_size_t next;/* 指向前一个内存块 */mem_size_t prev;/* 内存块是否被使用 1:被使用 0:未使用 */u8_t used;
};

初始化堆内存,将对内部组织成内存块链表。

/* 内存堆初始化 */
void mem_init(void)
{struct mem *mem;/* 内存堆地址 */ram = LWIP_MEM_ALIGN(ram_heap);/* 将内存堆首部组织成一个内存块,该内存块包含整个内存堆有效区域 */mem = (struct mem *)ram;mem->next = MEM_SIZE_ALIGNED;mem->prev = 0;mem->used = 0;/* 在内存堆尾部组织成一个内存块 */ram_end = (struct mem *)&ram[MEM_SIZE_ALIGNED];ram_end->used = 1;ram_end->next = MEM_SIZE_ALIGNED;ram_end->prev = MEM_SIZE_ALIGNED;/* 更新地址最低的空闲内存块 */lfree = (struct mem *)ram;
}

内存分配,从链表中找到一个合适的内存块,分配给程序。将剩下的内存组织成新的内存块,并挂接到链表。

/* 申请内存 */
void *mem_malloc(mem_size_t size)
{mem_size_t ptr, ptr2;struct mem *mem, *mem2;/* 内存申请不能为0 */if (size == 0) {return NULL;}/* 申请大小字节对齐 */size = LWIP_MEM_ALIGN_SIZE(size);/* 申请大小最小12字节 */if(size < MIN_SIZE_ALIGNED) {size = MIN_SIZE_ALIGNED;}/* 内存申请不能超过内存堆 */if (size > MEM_SIZE_ALIGNED) {return NULL;}/* 遍历所有内存块 */for (ptr = (u8_t *)lfree - ram; ptr < MEM_SIZE_ALIGNED - size; ptr = ((struct mem *)&ram[ptr])->next) {/* 内存块地址 */mem = (struct mem *)&ram[ptr];/* 找出合适的内存块 */if ((!mem->used) && (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) {/* 内存块过大 */if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED)) {/* 将多出来的内存重新组织成内存块 */ptr2 = ptr + SIZEOF_STRUCT_MEM + size;mem2 = (struct mem *)&ram[ptr2];mem2->used = 0;mem2->next = mem->next;mem2->prev = ptr;/* 当前内存块标记为已使用,并将新内存块插入链表 */mem->next = ptr2;mem->used = 1;if (mem2->next != MEM_SIZE_ALIGNED) {((struct mem *)&ram[mem2->next])->prev = ptr2;}} else {/* 当前内存块标记为已使用 */mem->used = 1;}/* 当前内存块为地址最低的空闲内存块 */if (mem == lfree) {/* 更新地址最低的空闲内存块 */while (lfree->used && lfree != ram_end) {lfree = (struct mem *)&ram[lfree->next];}}/* 返回内存地址 */return (u8_t *)mem + SIZEOF_STRUCT_MEM;}}return NULL;
}

释放内存,将内存重新组织成内存块插入链表。相邻内存堆都空闲,需要合并。

/* 释放内存 */
void mem_free(void *rmem)
{struct mem *mem;if (rmem == NULL) {return;}/* 内存地址必须在内存堆中 */if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) {return;}/* 通过内存地址找出内存块结构体指针 */mem = (struct mem *)((u8_t *)rmem - SIZEOF_STRUCT_MEM);/* 将内存块标识为空闲 */mem->used = 0;/* 更新地址最低空闲块 */if (mem < lfree) {lfree = mem;}/* 合并空闲块 */plug_holes(mem);
}
/* 合并空闲块 */
static void plug_holes(struct mem *mem)
{struct mem *nmem;struct mem *pmem;/* 下一个内存块 */nmem = (struct mem *)&ram[mem->next];/* 下一个内存块未被使用,并且不是最后一个内存块 */if (mem != nmem && nmem->used == 0 && (u8_t *)nmem != (u8_t *)ram_end) {/* 更新地址最低的空闲内存块 */if (lfree == nmem) {lfree = mem;}/* 和后一个内存块合并 */mem->next = nmem->next;((struct mem *)&ram[nmem->next])->prev = (u8_t *)mem - ram;}/* 上一个空闲块 */pmem = (struct mem *)&ram[mem->prev];/* 上一个内存块未使用 */if (pmem != mem && pmem->used == 0) {/* 更新地址最低的空闲内存块 */if (lfree == mem) {lfree = pmem;}/* 和上一个内存块合并 */pmem->next = mem->next;((struct mem *)&ram[mem->next])->prev = (u8_t *)pmem - ram;}
}

除了基本的分配和释放,协议栈还mem_realloc函数和mem_calloc函数

mem_realloc函数,能够收缩内存,将原有的内存缩小。缩小的部分重新组织成内存块,挂接到链表。

/* 收缩内存 */
void *mem_realloc(void *rmem, mem_size_t newsize)
{mem_size_t size;mem_size_t ptr, ptr2;struct mem *mem, *mem2;/* 新内存大小字节对齐 */newsize = LWIP_MEM_ALIGN_SIZE(newsize);/* 内存大小最小12字节 */if(newsize < MIN_SIZE_ALIGNED) {newsize = MIN_SIZE_ALIGNED;}/* 内存大小不能超过内存堆 */if (newsize > MEM_SIZE_ALIGNED) {return NULL;}/* 内存地址必须在内存堆中 */if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) {return rmem;}/* 通过内存地址找出内存块结构体指针 */mem = (struct mem *)((u8_t *)rmem - SIZEOF_STRUCT_MEM);/* 内存在内存堆中的偏移量 */ptr = (u8_t *)mem - ram;/* 计算内存块内存大小 */size = mem->next - ptr - SIZEOF_STRUCT_MEM;/* 只能缩小,不能扩展 */if (newsize > size) {return NULL;}/* 内存大小不变,直接返回 */if (newsize == size) {return rmem;}/* 下一个内存块空闲 */mem2 = (struct mem *)&ram[mem->next];if(mem2->used == 0) {mem_size_t next;/* 将切割下来的内存和下一个内存块合并 */next = mem2->next;ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize;if (lfree == mem2) {lfree = (struct mem *)&ram[ptr2];}mem2 = (struct mem *)&ram[ptr2];mem2->used = 0;mem2->next = next;mem2->prev = ptr;mem->next = ptr2;if (mem2->next != MEM_SIZE_ALIGNED) {((struct mem *)&ram[mem2->next])->prev = ptr2;}} /* 下一个内存块不空闲 */else if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED <= size) {/* 将切割下来的内存重新组织成内存块 */ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize;mem2 = (struct mem *)&ram[ptr2];if (mem2 < lfree) {lfree = mem2;}mem2->used = 0;mem2->next = mem->next;mem2->prev = ptr;mem->next = ptr2;if (mem2->next != MEM_SIZE_ALIGNED) {((struct mem *)&ram[mem2->next])->prev = ptr2;}}/* 返回内存指针 */return rmem;
}

mem_calloc函数,申请内存的同时,将内存清空。

/* 申请内存并清零 */
void *mem_calloc(mem_size_t count, mem_size_t size)
{void *p;p = mem_malloc(count * size);if (p) {memset(p, 0, count * size);}return p;
}

LwIP之动态内存堆相关推荐

  1. LwIP之动态内存池

    先看memp_t,这是一个枚举体,定义了所有内存池的类型. typedef enum { #define LWIP_MEMPOOL(name,num,size,desc) MEMP_##name, # ...

  2. LwIP 之五 详解动态内存管理 内存堆(mem.c/h)

    写在前面   目前网上有很多介绍LwIP内存的文章,但是绝大多数都不够详细,甚至很多介绍都是错误的!无论是代码的说明还是给出的图例,都欠佳!下面就从源代码,到图例详细进行说明.   目前,网络上多数文 ...

  3. LwIP 之六 详解内存池(memp.c/h)动态内存管理策略

      对于嵌入式开发来说,内存管理及使用是至关重要的,内存的使用多少.内存泄漏等时刻需要注意!合理的内存管理策略将从根本上决定内存分配和回收效率,最终决定系统的整体性能.LwIP 就提供了 动态内存堆管 ...

  4. python 指针_C++的动态内存:C++的指针

    在C++里,指针(pointer)变量被用来存储内存地址.C++要求使用特定的类型来定义指针.这个类型被用来指示需要如何去解释内存地址里的数据.我们已经知道,在计算机的内部,内存存储的是1和0,而C+ ...

  5. LwIP 之六 详解动态内存管理 内存池(memp.c/h)

      该文主要是接上一部分LwIP 之 详解动态内存管理 内存堆(mem.c/h),该部分许多内容需要用到上一篇的内容.该部分主要是详细介绍LwIP中的动态内存池.整个内存池的实现相较于内存堆来说,还是 ...

  6. 【C 语言】内存管理 ( 动态内存分配 | 栈 | 堆 | 静态存储区 | 内存布局 | 野指针 )

    相关文章链接 : 1.[嵌入式开发]C语言 指针数组 多维数组 2.[嵌入式开发]C语言 命令行参数 函数指针 gdb调试 3.[嵌入式开发]C语言 结构体相关 的 函数 指针 数组 4.[嵌入式开发 ...

  7. 内存区划分;内存分配;堆、栈概念分析;动态内存管理数据结构及程序样例;核心态与用户态...

    一. 在c中分为这几个存储区1.栈 - 由编译器自动分配释放 2.堆 - 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收 3.全局区(静态区),全局变量和静态变量的存储是放在一块的,初 ...

  8. c语言malloc引用类型作参数,c语言中动态内存分配malloc只在堆中分配一片内存.doc...

    c语言中动态内存分配malloc只在堆中分配一片内存 .C语言中动态内存分配(malloc)只在堆中分配一片内存,返回一个void指针(分配失败则返回0),并没有创建一个对象.使用时需要强制转换成恰当 ...

  9. 在堆区开辟内存(动态内存的开辟)

    目录 ​ 零.前言 1.基本概念 1.什么是动态内存 2.开辟动态内存的作用 1.在栈区开辟的空间 2.在堆区开辟空间 2.动态内存开辟的函数 1.void *malloc( size_t size ...

最新文章

  1. php写网页6,thinkphp6输出原始html内容 - 旗云号
  2. 区块链软件公司:区块链运用在金融上运用的优点有哪些?
  3. 软件架构阅读笔记15
  4. adc如何获取周期_ADI小课堂丨今天咱们实例分析一款精密型ADC
  5. springboot整合shiro-关于登出时,redis中缓存没有清理干净的问题
  6. 【转】SPSite、SPWeb对象模型(转winos.cn)
  7. linux查看日志命令_查看log日志基础命令
  8. php 给html 赋值,PHP+JavaScript+HTML变量之间赋值及传递
  9. 数据挖掘–聚类思维导图
  10. Unidac连接出错:命名管道提供程序:管道的另一端上无任何进程.
  11. 迅雷有linux版本吗,迅雷 - Linux Wiki
  12. VB实现List集合
  13. while在Java用法_while和do-while的使用方法
  14. 上网日志留存_中国移动5G上网日志留存系统招标:最高投标总限价10亿元
  15. 蓝桥杯 人民币金额大写 格式转换
  16. python3连接mysql获取ansible动态inventory脚本
  17. 场景编程集锦 - BMI指数与健身达人
  18. 网页抓取软件Wget用法详解
  19. ORACLE安装方法
  20. 安全运营 splunk入门

热门文章

  1. C++设计模式-使用Qt框架模拟策略模式(Strategy)商场促销
  2. C++工作笔记-stl中map基础用法(插入,遍历,删除)
  3. java的主函数在哪_打开一个别人的文件,一堆.java, 怎么知道main函数在哪里?
  4. python动态添加类方法_Python 动态添加类方法
  5. python怎么暂停爬虫_python Python爬虫防封杀方法集合
  6. python subplot_Python金融应用之图表制作(五)
  7. (数据分析三板斧)第一斧Numpy-第二节:生成数组、数组属性和切片
  8. (数据库系统概论|王珊)第二章关系数据库-第一节:关系数据结构及其形式化定义
  9. Linux系统编程8-18总结项目:完成一个简单的自己的shell
  10. 矩阵的运算及其运算规则