Skip to content

内存池-给任务分配固定大小的内存块

李述铜

1006字约3分钟

2025-08-30

任务在运行的过程中,有时需要动态的申请分配内存。大多数情况下,任务申请分配的内存块大小是固定的;于是,为了提升分配效率和速度,RT-Thread提供了内存池功能。

生活示例

想象你是一家工厂负责打包商品的工作人员,且客户购买的商品数量是可变的。如果根据客户的商品量找出刚刚能装下的包装盒;那么,此事将是一件费时费力的工作。

要么仓库里要提前准备好大大小小各种规格的包装盒;要么仓库里仅存放制作包装盒的材料,在需要时根据客户的需求制造大小刚好合适的盒子。

alt text

显然,无论是哪种方法,都不是高效的方法。如果换一种方式,仓库仅存储若干尺寸大小的盒子。当需要包装商品时,选一个能装下的盒子(哪怕有多余的空间)即可。这样一来,找盒子这一过程就非常快了。

内存池用途

类似地,在嵌入式系统中,任务的运行需要用到动态分配的内存块。不同场景下,任务所需的内存块大小可能不同。但是,在一个特定的产品中,整个程序中需要动态分配的内存块大小种类往往是极其有限的。

针对这种情况下,RT-Thread提供了内存池功能。简单来说,内存池机制就像这样的仓库:

alt text

内存池适用于以下典型场景:

  • 需要频繁申请/释放固定大小内存的情况
  • 多任务/中断共享内存资源
  • 内存使用需要快速响应(避免使用malloc()或free())
  • 嵌入式系统中需要确定性的内存分配方式

相较于malloc()或free()等堆分配方式,内存池具有更快、更稳定、更可控的优点。具体原因在下面介绍。

工作原理

RT-Thread的内存池由struct rt_mempool对象实现,其内部管理一个固定大小的内存块链表,通过分配与释放内存块来服务不同任务。

alt text

根据上图所示,当任务需要分配或释放内存时,只需要对block_list的头部进行操作(取出或插入头结点),分配效率极高。

特性描述
定长块所有内存块大小一致,由初始化时指定
预分配创建内存池时就分配全部内存,不会动态增长或缩减
快速响应分配和释放效率极高(无需遍历内存或碎片整理)
支持阻塞等待申请内存块时可设定超时等待

应用示例代码:分配和回收固定大小的消息结构体

#include "base.h"
#include <rtthread.h>

static struct rt_mailbox mbox;

static rt_ubase_t mbox_buffer[8];

struct sensor_data {
    uint8_t temp;
    uint8_t hum;
};
static uint8_t mem_pool[8*(RT_ALIGN(sizeof(struct sensor_data), RT_ALIGN_SIZE) + sizeof(uint8_t *))];
static struct rt_mempool mpool;

static void sensor_read (struct sensor_data * data) {
    data->temp = 20 + rt_tick_get();
    data->hum = 50 + rt_tick_get();
}

void sender_entry (void * param) {    
    while (1) {
        struct sensor_data * data = (struct sensor_data *)rt_mp_alloc(&mpool, RT_WAITING_FOREVER);
        sensor_read(data);
        rt_mb_send_wait(&mbox, (rt_ubase_t)data, RT_WAITING_FOREVER);
        
        rt_thread_mdelay(1000);
    }
}

void recv_entry (void * param) {
    while (1) {
        // recv
        struct sensor_data * data;
   
        rt_mb_recv(&mbox, (rt_ubase_t* )&data, RT_WAITING_FOREVER);
        
        rt_kprintf("temp: %d, hum: %d\n", data->temp, data->hum);
        rt_mp_free(data);
    }
}

int main(void) {
    hardware_init();
    
    rt_mb_init(&mbox, "mb", mbox_buffer, 8, RT_IPC_FLAG_FIFO);
    rt_mp_init(&mpool, "mp", mem_pool, sizeof(mem_pool), sizeof(struct sensor_data));
    
    rt_thread_t t1 = rt_thread_create("t1", sender_entry, RT_NULL, 4096, 10, 10);
    rt_thread_startup(t1);
    rt_thread_t t2 = rt_thread_create("t2", recv_entry, RT_NULL, 4096, 10, 10);
    rt_thread_startup(t2);
    
    return 0;
}