内存管理
定义与作用
内存池管理
- 基础定义
- 内存池是一种管理固定大小内存块的机制,主要用于减少碎片化,提高内存分配效率。
- 在 RT-Thread 中,内存池允许用户预分配一定数量的具有相同大小的内存块,应用程序可以从中快速分配和释放内存。
- 主要内容
- 创建和初始化内存池:定义一个内存池变量,通过 API 函数初始化,设定内存块大小和数量。
- 从内存池分配内存:应用程序可以从内存池中请求内存块,用于存储数据。
- 返回内存至内存池:使用完毕后,应将内存块返回至内存池,以供其他任务或用途重用。
内存堆管理
基础定义
- 内存堆管理提供更为灵活的内存分配方式,允许按需分配任意大小的内存区块。
- RT-Thread 支持动态内存分配(如 malloc、free 等标准 C 函数)以及自定义的动态内存管理函数。
主要内容
动态分配和释放内存:通过
rt_malloc()
和rt_free()
等 API,分配和释放内存。内存碎片整理:某些情况下,可以通过内存整理来减少内存碎片,提高内存利用率。
举例-通俗
- 在解释内存管理时,我们可以将其比作 用餐规则。
内存池管理 (Memory Pool Management)
场景
- 想象你在一个咖啡厅里,这个咖啡厅专门提供一种大小固定的咖啡杯(比如中杯),这些咖啡杯都是提前准备好的,并且放在一个特定的架子上。
- 顾客来到咖啡厅时,可以快速地从架子上拿一个中杯咖啡。
- 喝完后,顾客会把咖啡杯还回去,咖啡厅工作人员会清洗这些杯子并重新放回架子上,供下一位顾客使用。
对照关系
- 这里,咖啡杯相当于内存池管理中的内存块,架子上的所有咖啡杯集合就像是一个内存池。
- 咖啡杯的统一大小代表内存池中所有内存块的固定大小,而架子上的咖啡杯数量限制类似于内存池的容量。
- 顾客使用后归还的过程类似于内存块的回收和重用。
内存堆管理 (Heap Management)
场景
- 现在想象一个不同的场景,在一个大型的自助餐厅,顾客可以根据自己的需要选择不同大小的盘子来装食物。
- 你可以选择一个小盘子来装沙拉,一个中盘子来装主菜,还可以选择一个大盘子来装水果。
- 用完之后,你会把盘子放到指定的回收处,餐厅工作人员会清洗这些盘子并把它们放回原位,与内存池管理相比,但这次它们不再是固定大小的集合,而是根据需要被选取和放回的。
对照关系
- 在这个例子中,不同大小的盘子可以看作是动态分配的内存块,而自助餐厅提供的整体盘子资源类似于内存堆。
- 顾客根据需求选择不同大小的盘子,就像程序根据需要申请不同大小的内存空间。
- 用完后归还盘子的过程对应于释放内存空间,以便这些资源可以被其他顾客(即其他程序或进程)再次使用。
总结
- 内存池管理就像是咖啡厅中预先准备好的一堆固定大小的咖啡杯,适合于需要快速且频繁地分配和回收相同大小资源的场景;
- 而内存堆管理则类似于自助餐厅里各种大小的盘子,适用于需求多变,需要不同大小内存块的情况。
示例代码
两个代码的实现方式体现了区别,区别在于与存储空间数据交互的过程
内存池
内存堆
内存池管理
- 该程序会从内存池中分配内存块,并在每个块中填入从1开始的递增数字。
- 然后对其进行逐个取用与输出
#include <rtthread.h> // 引入RT-Thread头文件 #define POOL_SIZE 128 // 定义内存池的大小为128字节 #define BLOCK_SIZE 32 // 定义每个内存块的大小为32字节 #define BLOCK_COUNT 4 // 定义内存块的数量为4(示例中实际可用) // 定义内存池空间,实际存储内存块的地方 static rt_uint8_t mempool[POOL_SIZE]; // 定义内存池控制块,管理内存池的状态和属性 struct rt_mempool mp; int main(void) { void *block; // 定义一个指针用于接收分配的内存块地址 int i = 1; // 用于填充内存块的递增数字 // 初始化内存池 rt_mp_init(&mp, "mempool", &mempool, sizeof(mempool), BLOCK_SIZE); for (int j = 0; j < BLOCK_COUNT; j++) { // 从内存池分配一个内存块 block = rt_mp_alloc(&mp, RT_WAITING_FOREVER); if (block != RT_NULL) { // 将递增数字填入内存块 *(int *)block = i++; // 打印内存块中的数字,验证我们的操作 rt_kprintf("Block %d: %d\n", j + 1, *(int *)block); // 模拟一些处理过程,这里我们简单地延时 rt_thread_mdelay(RT_TICK_PER_SECOND); // 将内存块返回至内存池,释放内存块以供其他操作使用 rt_mp_free(block); } } return 0; }
内存堆管理
- 在这段代码中,我们循环
BLOCK_COUNT
次,每次都尝试从堆中分配一块大小足以存储一个整数的内存。- 如果分配成功,我们就把一个递增的数字存入这块内存,打印出这个数字,然后在下一次循环前释放这块内存。
- 请注意,使用堆内存管理时,您需要保证系统的堆大小足够大,能够满足您的分配请求。
- 您可以在系统初始化代码中设置堆大小。另外,频繁地分配和释放小块内存可能会导致内存碎片,从而影响系统性能。
#include <rtthread.h> // 引入RT-Thread头文件 #define BLOCK_COUNT 10 // 定义需要分配的内存块数量 int main(void) { void *block; // 定义一个指针用于接收分配的内存块地址 int i = 1; // 用于填充内存块的递增数字 for (int j = 0; j < BLOCK_COUNT; j++) { // 从内存堆中动态分配一块内存 block = rt_malloc(sizeof(int)); if (block != RT_NULL) { // 将递增数字填入内存块 *(int *)block = i++; // 打印内存块中的数字,验证我们的操作 rt_kprintf("Block %d: %d\n", j + 1, *(int *)block); // 模拟一些处理过程,这里我们简单地延时 rt_thread_mdelay(RT_TICK_PER_SECOND); // 释放内存块,返回它到内存堆 rt_free(block); } else { // 如果内存分配失败,打印错误信息 rt_kprintf("Memory allocation failed for block %d\n", j + 1); } } return 0; }
逐行解释 代码
内存池管理
#include <rtthread.h>
: 引入 RT-Thread 的主要功能。#define POOL_SIZE 128
: 设置内存池总大小为128字节。#define BLOCK_SIZE 32
: 设置每个内存块的大小为32字节。#define BLOCK_COUNT 10
: 设定内存块数量为10。static rt_uint8_t mempool[POOL_SIZE]
: 静态分配内存池数组。struct rt_mempool mp
: 定义内存池控制块。int main(void)
: 主函数开始。void *block
: 定义指向内存块的指针。rt_mp_init
: 初始化内存池。- 循环中,
rt_mp_alloc
从内存池分配内存,rt_kprintf
打印信息,rt_thread_mdelay
延时,rt_mp_free
释放内存。#include <rtthread.h> // 引入 RT-Thread 的主要头文件,提供操作系统功能 #define POOL_SIZE 128 // 定义内存池的大小为128字节 #define BLOCK_SIZE 32 // 定义每个内存块的大小为32字节 #define BLOCK_COUNT 10 // 定义内存池能够容纳的内存块数量为10(注意:此处定义并未直接用于下面的代码,但有助于理解内存池的容量) static rt_uint8_t mempool[POOL_SIZE]; // 静态分配128字节内存作为内存池的存储空间 struct rt_mempool mp; // 声明一个内存池控制块变量 int main(void) { void *block; // 用于接收分配的内存块地址的指针 // 初始化内存池,参数分别为:内存池控制块地址、内存池名称、内存池的存储空间地址、内存池的总大小、每个内存块的大小 rt_mp_init(&mp, "mempool", &mempool, sizeof(mempool), BLOCK_SIZE); // 循环体:模拟内存块的分配和使用 while(1) { block = rt_mp_alloc(&mp, RT_WAITING_FOREVER); // 从内存池中分配一个内存块,如果没有可用内存块则永久等待 if (block != RT_NULL) // 检查是否成功分配内存块 { // ... 在这里使用内存块进行操作 ... rt_kprintf("Block allocated\n"); // 打印内存块已被分配的信息 rt_thread_mdelay(1000); // 线程延迟1000毫秒(1秒) rt_mp_free(block); // 将内存块归还给内存池 } } // 实际应用中不会到达这里,但理论上应释放所有资源 return 0; // 返回0,结束主函数 }
内存堆管理
#include <rtthread.h>
: 引入 RT-Thread 的头文件。#define BLOCK_COUNT 10
: 定义需要处理的内存块数量。int main(void)
: 主函数入口。void *block
: 定义一个通用指针用于指向分配的内存块。int i = 1
: 初始化递增的数字。\- 循环使用
rt_malloc
从堆中分配内存,存储数字并打印,然后用rt_free
释放内存。rt_device_find
,rt_device_open
,rt_mp_init
,rt_mp_alloc
,rt_mp_free
,rt_thread_mdelay
等函数分别用于设备查找、打开、内存分配与释放及线程延时。
#include <rtthread.h> // 引入 RT-Thread 的主要头文件 int main(void) { void *block; // 定义一个指针,用于指向分配的内存块 int i = 1; // 定义一个整数变量,用于在内存块中存储递增的数字 while(1) { block = rt_malloc(sizeof(int)); // 从堆中分配足够存储一个整数的内存块 if (block != RT_NULL) // 检查内存块是否成功分配 { *(int *)block = i; // 将递增的数字存储在分配的内存块中 rt_kprintf("Allocated int: %d\n", *(int *)block); // 打印存储在内存块中的数字 i++; // 递增数字 rt_thread_mdelay(1000); // 线程延迟1000毫秒(1秒) rt_free(block); // 释放内存块,将其归还给堆 } } // 在实际的应用程序中,这里应该有代码来清理和释放所有分配的资源 return 0; // 返回0,正常结束主函数 }