目的
1 了解 RT-Thread 内存管理的方式
2 了解动态内存堆管理 和 静态内存池管理
正文:
内存管理的特点
1 分配内存的时间必须确定
2 避免内存分配的碎片化问题
3 内存资源差异较大
RT-Thread 三种管理方式
1 小内存 小内存管理法 <2MB
2 大内存 slab 管理法
3 多内存 memheap 管理法
简单介绍:
slab 是 linux 一种内存分配机制,其特点就是针对经常分配并释放的对象,统一由 slab 管理;通俗而言,就是直接分配一个区域,并且规划了大小,需要使用的时候直接分配对应空闲区域地址即可,这样就避免了碎片化这个问题
注意:
1 不要在中断服务例程中分配或释放动态内存块
1 小内存
magic --》 0x1ea0 内存保护字,正常只有内存管理器会使用,如果被改写则认定内存被非法改写
used: 指出内存块是否已经分配
2 slab 管理算法
分配流程:
1 先检查 zone 链表是否为空,是则分配一个新的 zone
2 zone 非空,第一个 zone 节点必然有空闲块(否则不会存在在 zone 中)就取来使用
3 内存释放则需要将内存块链接到 zone 空闲内存块链表中
4 当 zone 空闲块到达一定数量之后,系统将会释放到页面分配器中
3 memheap 管理算法
将多个内存块,加入到 memheap_item 中进行粘合,形成一个完整的操作内存堆
内存堆的管理方式
申请内存 rt_malloc/realloc/calloc
释放内存 rt_free / rt_free_sethook
rt_realloc 裁剪/增加内存
rt_calloc 分配多内存块
内存池
作用:用于分配大量大小相同的小内存块,可以极大地加速内存分配与释放的速度,避免内存碎片化
内存池控制块由结构体 struct rt_mempool 表示。另外一种 C 表达方式 rt_mp_t,表示的是内存块句柄
struct rt_mempool {
struct rt_object parent;
void *start_address; /* 内存池数据区域开始地址 */
rt_size_t size; /* 内存池数据区域大小 */
rt_size_t block_size; /* 内存块大小 */
rt_uint8_t *block_list; /* 内存块列表 */
/* 内存池数据区域中能够容纳的最大内存块数 */
rt_size_t block_total_count; /* 内存池中空闲的内存块数 */
rt_size_t block_free_count; /* 因为内存块不可用而挂起的线程列表 */
rt_list_t suspend_thread; /* 因为内存块不可用而挂起的线程数 */
rt_size_t suspend_thread_count;
};
typedef struct rt_mempool* rt_mp_t;
管理方式依旧:
创建/初始化 rt_mp_create/init()
分配内存块 rt_mp_alloc()
释放内存块 rt_mp_free()
删除/脱离 rt_mp_delete/detach()
内存池块个数 = size / (block_size + 4 链表指针大小),计算结果取整数。例如:内存池数据区总大小 size 设为 4096 字节,内存块大小 block_size 设为 80 字节;则申请的内存块个数为 4096/ (80+4)= 48 个
#include <rtthread.h>
static rt_uint8_t *ptr[50];
static rt_uint8_t mempool[4096];
static struct rt_mempool mp;
#define THREAD_PRIORITY 25
#define THREAD_STACK_SIZE 512
#define THREAD_TIMESLICE 5
/* 指向线程控制块的指针 */
static rt_thread_t tid1 = RT_NULL;
static rt_thread_t tid2 = RT_NULL;
/* 线程 1 入口 */
static void thread1_mp_alloc(void *parameter) {
int i;
for (i = 0 ; i < 50 ; i++) {
if (ptr[i] == RT_NULL) {
/* 试图申请内存块 50 次,当申请不到内存块时, 线程 1 挂起,转至线程 2 运行 */
ptr[i] = rt_mp_alloc(&mp, RT_WAITING_FOREVER);
if (ptr[i] != RT_NULL)
rt_kprintf("allocate No.%d\n", i);
}
}
}
/* 线程 2 入口,线程 2 的优先级比线程 1 低,应该线程 1 先获得执行。*/
static void thread2_mp_release(void *parameter) {
int i;
rt_kprintf("thread2 try to release block\n");
for (i = 0; i < 50 ; i++) { /* 释放所有分配成功的内存块 */
if (ptr[i] != RT_NULL) {
rt_kprintf("release block %d\n", i);
rt_mp_free(ptr[i]);
ptr[i] = RT_NULL;
}
}
}
int mempool_sample(void) {
int i;
for (i = 0; i < 50; i ++)
ptr[i] = RT_NULL; /* 初始化内存池对象 */
rt_mp_init(&mp, "mp1", &mempool[0], sizeof(mempool), 80); /* 创建线程 1:申请内存池 */
tid1 = rt_thread_create("thread1", thread1_mp_alloc, RT_NULL, THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE);
if (tid1 != RT_NULL)
rt_thread_startup(tid1);
/* 创建线程 2:释放内存池 */
tid2 = rt_thread_create("thread2", thread2_mp_release, RT_NULL, THREAD_STACK_SIZE, THREAD_PRIORITY + 1,THREAD_TIMESLICE);
if (tid2 != RT_NULL)
rt_thread_startup(tid2);
return 0;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(mempool_sample, mempool sample);