LOS_MemBox
membox — 固定大小内存池 — LiteOS Membox
前置阅读:osal_kmalloc — 先理解 kmalloc 的碎片问题,才知道为什么需要 membox
学习目标
- 理解 membox 的原理——预分配固定大小槽位 + 位图管理,alloc/free 都是 O(1),零碎片
- 掌握
LOS_MemboxInit(pool, poolSize, blkSize)→LOS_MemboxAlloc(pool)→LOS_MemboxFree(pool, box)的调用链 - 能够在高频小对象场景中正确选择 membox 而非 kmalloc
基本概念
membox 原理
membox 的思路很简单:预先把一块连续内存切成大小相同的槽,用位图跟踪每个槽的状态。
flowchart TD
subgraph 内存池 1024B
direction LR
S0[槽0 64B]
S1[槽1 64B]
S2[槽2 64B]
S3[槽3 64B]
SD[...]
S15[槽15 64B]
end
subgraph 位图 16bit
direction LR
B0["0"]
B1["1"]
B2["0"]
B3["1"]
BD["..."]
B15["0"]
end
S0 -.-> B0
S1 -.-> B1
S2 -.-> B2
- alloc = 找位图中第一个 0(空闲),标为 1,返回对应槽地址——O(1)
- free = 根据槽地址算出位图索引,标为 0——O(1)
- 因为所有槽大小完全相同,永远不会碎片化
典型使用场景
| 场景 | 块大小 | 为什么用 membox |
|---|---|---|
| 消息帧缓冲区 | 64B | 每秒数千次 alloc/free,kmalloc 很快碎片化 |
| 定时器结构体池 | 32B | 动态创建/销毁定时器,生命周期短 |
| 网络包描述符 | 128B | 高频收发,需要稳定分配速度 |
不适合的场景:需要可变大小——如 OTA 固件暂存(128KB),用 kmalloc。
membox vs kmalloc
| 对比项 | membox | kmalloc |
|---|---|---|
| 分配速度 | O(1),纳秒级 | O(n),微秒级 |
| 碎片化 | 永远不会 | 长期运行会碎片化 |
| 块大小 | 固定,初始化时确定 | 可变,每次指定 |
| 内存利用率 | 可能浪费(未满槽也占着) | 按需分配,利用率高 |
| API 层 | LiteOS 原生,los_membox.h |
OSAL 封装,osal_addr.h |
| ISR 可用 | 否(内部可能关中断) | 是(GFP_ATOMIC) |
flowchart LR
A[需要动态内存] --> B{块大小固定?}
B -->|否| C[kmalloc]
B -->|是| D{分配频率?}
D -->|低频 偶尔| C
D -->|高频 每秒百次+| E[membox]
涉及 API
| API | 谁调用 | 用途 | 头文件 |
|---|---|---|---|
UINT32 LOS_MemboxInit(VOID *pool, UINT32 poolSize, UINT32 blkSize) |
入口 | 初始化 membox 池 | los_membox.h |
VOID *LOS_MemboxAlloc(VOID *pool) |
任务 | 分配一个固定大小块(O(1)) | los_membox.h |
UINT32 LOS_MemboxFree(VOID *pool, VOID *box) |
任务 | 释放一个块(O(1)) | los_membox.h |
membox 是 LiteOS 原生接口,无 OSAL 封装。使用时
#include "los_membox.h"。
案例说明
做什么
创建一个 64B × 16 槽的 membox,循环 alloc/free 1000 次,统计平均耗时。同时用 kmalloc 做相同操作对比速度。验证 membox 的 O(1) 零碎片特性。
规格与功能
| 规格项 | 说明 |
|---|---|
| 池大小 | 64B × 16 = 1024B + 位图开销 |
| 块大小 | 64B(覆盖大多数传感器帧和消息结构体) |
| 测试循环 | 1000 次 alloc + free |
| 对比基准 | kmalloc(64, GFP_KERNEL) + kfree 1000 次 |
| 对齐 | __attribute__((aligned(4))) |
程序运行流程:
- 静态数组
g_pool[1024]对齐 →LOS_MemboxInit - 循环 1000 次:alloc → 写入测试数据 → free
- 记录总 ticks,计算平均耗时
- 用 kmalloc 再做一遍,对比输出
案例流程
flowchart TD
I[LOS_MemboxInit pool, 1024, 64] --> L
L[循环 1000 次] --> A[LOS_MemboxAlloc]
A --> U[写入测试数据]
U --> F[LOS_MemboxFree]
F --> D{1000 次?}
D -->|否| A
D -->|是| P[打印平均耗时]
案例操作指导
第一步:编译
第二步:烧录
第三步:验证
串口输出类似:
[membox] 1000 alloc+free: 350 ticks (avg 0.35 ticks/op)
[kmalloc] 1000 alloc+free: 8500 ticks (avg 8.50 ticks/op)
[membox] speedup: 24x
membox 比 kmalloc 快约 20~30 倍(取决于堆的碎片程度)。
关键配置
| 参数 | 值 | 说明 |
|---|---|---|
块大小 blkSize |
64B | 根据实际需求确定,所有 alloc 出来的块都这么大 |
| 槽数量 | 16 | 太少频繁满、太多浪费——按峰值并发量 × 1.5 估算 |
池大小 poolSize |
blkSize × 槽数 + 位图开销 |
init 时指定,静态数组大小要匹配 |
| 内存对齐 | 4 字节 | __attribute__((aligned(4))) 或 __aligned(4) |
| ISR 中 | 不建议 | membox 内部可能关中断,ISR 中用 kmalloc(GFP_ATOMIC) |
代码详解
membox 初始化
#include "los_membox.h"
#define BLK_SIZE 64
#define BLK_NUM 16
#define POOL_SIZE (BLK_SIZE * BLK_NUM)
/* 静态数组——编译期确定大小,放在 .bss 或 .data */
static uint8_t g_membox_pool[POOL_SIZE] __attribute__((aligned(4)));
static void app_entry(void)
{
UINT32 ret;
/* LOS_MemboxInit: pool, poolSize, blkSize
poolSize 必须能容纳 blkSize × 数量 + 位图
建议比 BLK_SIZE * BLK_NUM 稍大一些 */
ret = LOS_MemboxInit(g_membox_pool, sizeof(g_membox_pool), BLK_SIZE);
if (ret != LOS_OK) {
printf("membox init failed!\n");
return;
}
printf("membox init ok: %u slots of %u bytes\n", BLK_NUM, BLK_SIZE);
}
app_run(app_entry);
分配与释放
void membox_demo(void)
{
void *blocks[BLK_NUM] = {NULL};
int i;
/* 分配全部 16 个槽 */
for (i = 0; i < BLK_NUM; i++) {
blocks[i] = LOS_MemboxAlloc(g_membox_pool);
if (blocks[i] == NULL) {
printf("alloc failed at slot %d\n", i);
break;
}
/* 使用:写入测试数据 */
memset(blocks[i], 0xA5, BLK_SIZE);
}
/* 释放前几个 */
for (i = 0; i < 5; i++) {
if (blocks[i] != NULL) {
LOS_MemboxFree(g_membox_pool, blocks[i]);
blocks[i] = NULL;
}
}
/* 再 alloc——从刚释放的槽中分配,O(1) */
void *new_block = LOS_MemboxAlloc(g_membox_pool);
/* new_block 指向刚才释放的某个槽 */
}
注意
LOS_MemboxFree需要传pool指针——free(pool, box),和 C 标准库free(ptr)不同。pool 指针必须和 alloc 时一致。
与 kmalloc 的速度对比
void benchmark(void)
{
unsigned int start, elapsed;
void *ptr;
int i;
/* membox 测速 */
start = osal_get_tick();
for (i = 0; i < 1000; i++) {
ptr = LOS_MemboxAlloc(g_membox_pool);
LOS_MemboxFree(g_membox_pool, ptr);
}
elapsed = osal_get_tick() - start;
printf("[membox] 1000 ops: %u ticks\n", elapsed);
/* kmalloc 测速 */
start = osal_get_tick();
for (i = 0; i < 1000; i++) {
ptr = osal_kmalloc(BLK_SIZE, OSAL_GFP_KERNEL);
osal_kfree(ptr);
}
elapsed = osal_get_tick() - start;
printf("[kmalloc] 1000 ops: %u ticks\n", elapsed);
}
选择策略总结
| 你的需求 | 推荐 |
|---|---|
| 块大小固定,每秒 alloc/free 百次以上 | membox |
| 块大小可变,偶尔分配 | kmalloc |
| 块大小固定,但偶尔分配 | 都可以,kmalloc 更简单 |
| ISR 中需要分配 | kmalloc(GFP_ATOMIC)(membox 不推荐 ISR) |
| 需要 OSAL 统一接口 | kmalloc(membox 是 LiteOS 原生) |