LOADING
1885 字
9 分钟
ChibiOS/RT 内存管理

嵌入式系统中内存资源有限,ChibiOS/RT 提供了多种内存管理策略,从静态分配到动态堆,再到固定大小内存池,开发者可以根据应用场景选择最合适的方案。

静态内存分配#

静态内存分配在编译时确定内存布局,具有确定性高、无碎片、无运行时开销的优点,是嵌入式系统的首选。

对象静态初始化#

ChibiOS/RT 中几乎所有内核对象都支持静态初始化,无需动态分配:

/* 线程静态初始化 */
static THD_WORKING_AREA(waThread1, 256);
static THD_FUNCTION(ThreadFunc, arg) {
(void)arg;
/* 线程工作 */
}
/* 静态启动线程 */
static thread_t *tp1;
tp1 = chThdCreateStatic(waThread1, sizeof(waThread1),
NORMALPRIO, ThreadFunc, NULL);

内存静态声明#

使用 CH_MEM_ALIGN_SIZE 宏确保内存对齐:

/* 静态声明一块内存池 */
#define POOL_SIZE 16
static CH_MEM_ALIGN_SIZE(uint8_t pool_buffer[POOL_SIZE * 32]);
/* 静态声明消息队列 */
static msg_t mq_buffer[16];
static mailbox_t mq1;

静态分配适用于生命周期固定、数量确定的对象,如线程工作区、消息缓冲区等。

堆管理#

堆管理提供动态内存分配能力,适合生命周期不确定的对象。

堆数据结构#

typedef struct {
virtual_timer_t vt; /* 用于碎片整理的虚拟定时器 */
memblk_t *free; /* 空闲块链表 */
uint32_t size; /* 堆总大小 */
uint32_t free_size; /* 当前空闲大小 */
} memory_heap_t;

堆操作#

/* 初始化堆 */
void chHeapInit(memory_heap_t *heapp, void *buf, size_t size);
/* 从堆中分配内存 */
void *chHeapAlloc(memory_heap_t *heapp, size_t size);
/* 释放内存 */
void chHeapFree(void *p);
/* 查询堆状态 */
size_t chHeapGetFreeSize(memory_heap_t *heapp);
size_t chHeapGetSize(memory_heap_t *heapp);

堆管理示例#

/* 定义堆空间 */
static CH_MEM_ALIGN_SIZE(uint8_t heap_buffer[4096]);
static memory_heap_t heap1;
/* 初始化堆 */
static void heap_init(void) {
chHeapInit(&heap1, heap_buffer, sizeof(heap_buffer));
}
/* 动态分配传感器数据结构 */
typedef struct {
uint32_t timestamp;
int16_t temperature;
int16_t humidity;
uint8_t status;
} sensor_data_t;
static sensor_data_t *allocate_sensor_data(void) {
sensor_data_t *data = chHeapAlloc(&heap1, sizeof(sensor_data_t));
if (data != NULL) {
data->timestamp = 0;
data->temperature = 0;
data->humidity = 0;
data->status = 0;
}
return data;
}
static void release_sensor_data(sensor_data_t *data) {
if (data != NULL) {
chHeapFree(data);
}
}

碎片化处理#

ChibiOS/RT 堆管理器支持自动碎片整理:

/* 启用堆碎片整理(通过虚拟定时器定期执行) */
/* chHeapInit 时自动配置,无需手动干预 */
/* 手动触发碎片整理 */
chHeapDefragment(&heap1);

堆管理的主要缺点是可能产生内存碎片,且分配时间不确定。对于实时性要求高的系统,应谨慎使用或配合内存池。

内存池#

内存池是 ChibiOS/RT 中最常用的动态内存分配方式,提供固定大小对象的快速分配,无碎片、确定性强。

内存池数据结构#

typedef struct {
void *next; /* 空闲对象链表 */
sysinterval_t size; /* 对象大小 */
sysinterval_t obj_cnt; /* 对象总数 */
sysinterval_t free_cnt; /* 空闲对象数 */
} memory_pool_t;

内存池操作#

/* 初始化内存池 */
void chPoolInit(memory_pool_t *mp, size_t size, void *buf);
/* 从池中分配对象 */
void *chPoolAlloc(memory_pool_t *mp);
/* 释放对象回池 */
void chPoolFree(memory_pool_t *mp, void *obj);
/* 批量初始化池 */
void chPoolLoadArray(memory_pool_t *mp, void *objects, size_t count);
/* 查询空闲对象数 */
sysinterval_t chPoolGetFreeCount(memory_pool_t *mp);

内存池示例#

#define MSG_POOL_SIZE 8
#define MSG_SIZE 64
/* 池缓冲区 */
static CH_MEM_ALIGN_SIZE(uint8_t msg_pool_buf[MSG_POOL_SIZE * MSG_SIZE]);
static memory_pool_t msg_pool;
/* 消息结构 */
typedef struct {
uint32_t id;
uint8_t data[MSG_SIZE - sizeof(uint32_t) - sizeof(void *)];
void *next; /* 链表指针 */
} message_t;
/* 初始化消息池 */
static void msg_pool_init(void) {
chPoolInit(&msg_pool, sizeof(message_t), msg_pool_buf);
}
/* 分配消息 */
static message_t *alloc_message(void) {
message_t *msg = chPoolAlloc(&msg_pool);
if (msg != NULL) {
msg->id = 0;
msg->next = NULL;
}
return msg;
}
/* 释放消息 */
static void free_message(message_t *msg) {
if (msg != NULL) {
chPoolFree(&msg_pool, msg);
}
}
/* 高级用法:使用静态内存池声明宏 */
/* 消息池和对象定义一步完成 */
CH_POOL_DEFINE(msg_pool_macro, MSG_SIZE, MSG_POOL_SIZE,
msg_pool_macro_buf);

内存池与队列结合#

内存池常与消息队列结合使用,实现无阻塞的消息传递:

#define MAILBOX_SIZE 16
static msg_t mb_buffer[MAILBOX_SIZE];
static mailbox_t mb1;
static memory_pool_t msg_pool;
static CH_MEM_ALIGN_SIZE(uint8_t msg_buf[MAILBOX_SIZE * sizeof(message_t)]);
/* 初始化 */
static void messaging_init(void) {
chPoolInit(&msg_pool, sizeof(message_t), msg_buf);
chMBObjectInit(&mb1, mb_buffer, MAILBOX_SIZE);
}
/* 发送消息 */
static msg_t send_message(uint32_t id, uint8_t *data, size_t len) {
message_t *msg = chPoolAlloc(&msg_pool);
if (msg == NULL) {
return MSG_TIMEOUT;
}
msg->id = id;
memcpy(msg->data, data, len);
msg_t status = chMBPost(&mb1, (msg_t)msg, TIME_INFINITE);
if (status != MSG_OK) {
chPoolFree(&msg_pool, msg);
}
return status;
}
/* 接收消息 */
static void receive_message(void) {
msg_t msg_ptr;
msg_t status = chMBFetch(&mb1, &msg_ptr, TIME_INFINITE);
if (status == MSG_OK) {
message_t *msg = (message_t *)msg_ptr;
process_message(msg);
chPoolFree(&msg_pool, msg);
}
}

C++ 内存管理#

ChibiOS/RT 支持 C++ 环境,提供自定义的内存分配运算符。

运算符重载#

/* 包含内存分配头文件 */
#include <ch.hpp>
#include <chcore-alloc.h>
/* 全局 new/delete 运算符重载 */
void *operator new(size_t size) {
return chHeapAlloc(NULL, size);
}
void *operator new[](size_t size) {
return chHeapAlloc(NULL, size);
}
void operator delete(void *p) noexcept {
chHeapFree(p);
}
void operator delete[](void *p) noexcept {
chHeapFree(p);
}
void operator delete(void *p, size_t) noexcept {
chHeapFree(p);
}
void operator delete[](void *p, size_t) noexcept {
chHeapFree(p);
}

C++ 对象管理#

/* 使用 CH_MEM_ALIGN_SIZE 确保对齐 */
class Sensor {
public:
Sensor() : value_(0) {}
void update() { value_ = read_sensor(); }
int get_value() const { return value_; }
private:
int value_;
};
/* 动态创建 C++ 对象 */
static void create_sensor_dynamically(void) {
Sensor *s = new Sensor();
s->update();
int val = s->get_value();
delete s;
}
/* 静态创建 C++ 对象(避免动态分配) */
static Sensor static_sensor;
/* 对象池化(配合内存池使用) */
class PooledObject {
public:
void *operator new(size_t size) {
(void)size;
return chPoolAlloc(&obj_pool);
}
void operator delete(void *p) {
chPoolFree(&obj_pool, p);
}
/* ... 成员函数 ... */
private:
static memory_pool_t obj_pool;
static CH_MEM_ALIGN_SIZE(uint8_t obj_buf[16 * sizeof(PooledObject)]);
};

内存对齐#

内存对齐是嵌入式编程中的重要概念,不对齐的内存访问可能导致硬件异常或性能下降。

对齐类型#

/* 线程工作区对齐类型 */
typedef void *stkalign_t;
/* 内存对齐宏 */
#define MEM_ALIGN_SIZE sizeof(void *) /* 通常是 4 或 8 字节 */
/* 确保缓冲区对齐 */
static CH_MEM_ALIGN_SIZE(uint8_t aligned_buffer[1024]);
/* 手动对齐计算 */
#define ALIGN_UP(x, align) (((x) + (align) - 1) & ~((align) - 1))

对齐要求#

  • ARM Cortex-M 要求 4 字节对齐(Cortex-M3/M4)或 2 字节对齐(Cortex-M0)
  • DMA 传输通常要求 1024 字节或更高对齐
  • 浮点运算要求 4 字节对齐
  • CH_MEM_ALIGN_SIZE 确保所有内核对象满足最大对齐要求
/* 检查对齐 */
static bool is_aligned(void *ptr, size_t align) {
return ((uintptr_t)ptr % align) == 0;
}
/* 使用示例 */
static uint8_t buffer[256] __attribute__((aligned(1024))); /* DMA 缓冲区 */

内存管理最佳实践#

避免碎片#

/* 好的做法:使用内存池 */
static memory_pool_t event_pool;
static CH_MEM_ALIGN_SIZE(uint8_t event_buf[32 * sizeof(event_t)]);
/* 避免:频繁 chHeapAlloc/chHeapFree 导致碎片 */

选择合适策略#

场景推荐策略原因
线程工作区静态分配生命周期固定,确定性最高
通信缓冲区内存池固定大小,快速分配释放
配置数据大小不固定,偶尔分配
ISR 缓冲区静态或池不能在 ISR 中动态分配

静态 vs 动态#

/* 静态方式:编译时确定,无运行时开销 */
static THD_WORKING_AREA(waThread, 256);
/* 动态方式:灵活但有开销 */
void *stack = chHeapAlloc(&heap, THD_STACK_SIZE(stacksize));

常见陷阱#

  1. ISR 中不能使用阻塞性内存操作chHeapAlloc 在堆满时可能阻塞
  2. 未对齐的内存释放:确保释放的指针来自分配函数
  3. 重复释放:可能导致堆损坏
  4. 缓冲区溢出:使用内存池时确保对象大小足够
/* 错误:ISR 中分配 */
void isr_handler(void) {
/* 错误!不能在 ISR 中调用可能阻塞的分配 */
// void *p = chHeapAlloc(&heap, size);
/* 正确:使用预分配的缓冲区 */
static uint8_t isr_buffer[64];
process_data(isr_buffer, sizeof(isr_buffer));
}

总结#

ChibiOS/RT 的内存管理策略从简到繁,覆盖了嵌入式开发的各种需求:

机制确定性碎片开销适用场景
静态分配最高生命周期固定对象
内存池固定大小对象
堆管理不确定可能大小不定对象
C++ 运算符取决于实现取决于实现C++ 对象管理

在实际项目中,建议优先使用静态分配和内存池,将堆管理作为最后手段。良好的内存管理策略是系统稳定运行的基础。

ChibiOS/RT 内存管理
/posts/chibios-rt-memory/
作者
JJZBQA
发布于
2024-12-20
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时