LOADING
2652 字
13 分钟
ChibiOS/RT OS库与IPC机制

ChibiOS/RT OS库与IPC机制#

一、OS库概述#

ChibiOS/RT的OS库(OS Library)是内核之上的高级组件集合,提供了一系列可选的IPC(进程间通信)机制和资源管理工具。OS库通过 oslib.h 头文件对外暴露接口,需要在系统初始化时调用 chLibInit() 进行初始化。

OS库的组件是可选的,可以通过 oslibconf.h 配置文件逐个启用或禁用。这种模块化设计使得开发者可以根据项目需求裁剪系统,节省宝贵的ROM和RAM资源。

#include "oslib.h"
void system_init(void) {
/* 初始化内核 */
halInit();
chSysInit();
/* 初始化OS库 */
chLibInit();
}

OS库提供的主要IPC机制包括:

机制数据类型适用场景
队列(Queue)消息(msg_t通用消息传递
对象队列(Objects Queue)对象指针对象池管理
对象FIFO(Objects FIFO)固定大小对象高性能对象传递
对象缓存(Object Cache)任意对象缓存与复用
动态对象工厂(Factory)动态对象对象生命周期管理

二、队列(Queue)#

队列是最基础的IPC机制,用于在线程之间传递 msg_t 类型的消息。队列采用FIFO(先进先出)策略,支持超时等待。

2.1 数据结构#

typedef struct {
msg_t *buf; /* 消息缓冲区指针 */
cnt_t cnt; /* 缓冲区大小 */
cnt_t wrptr; /* 写指针 */
cnt_t rdptr; /* 读指针 */
cnt_t n; /* 当前消息数量 */
/* ... 内部等待队列 ... */
} queue_t;

2.2 初始化与基本操作#

/* 定义缓冲区和队列 */
#define QUEUE_SIZE 16
static msg_t queue_buffer[QUEUE_SIZE];
static queue_t queue;
void queue_example_init(void) {
/* 初始化队列 */
chQueueInit(&queue, queue_buffer, QUEUE_SIZE);
}
/* 生产者线程:发送消息 */
static THD_WORKING_AREA(wa_producer, 256);
static THD_FUNCTION(producer_thread, arg) {
(void)arg;
msg_t msg = 0;
while (true) {
/* 发送消息,超时100个系统滴答 */
msg_t result = chQueueSendTimeout(&queue, &msg, TIME_MS2I(100));
if (result == MSG_OK) {
/* 发送成功 */
}
msg++;
chThdSleepMilliseconds(50);
}
}
/* 消费者线程:接收消息 */
static THD_WORKING_AREA(wa_consumer, 256);
static THD_FUNCTION(consumer_thread, arg) {
(void)arg;
msg_t msg;
while (true) {
/* 无限等待接收消息 */
msg_t result = chQueueReceiveTimeout(&queue, &msg, TIME_INFINITE);
if (result == MSG_OK) {
/* 处理接收到的消息 */
chprintf((BaseSequentialStream *)&SD1, "Received: %d\r\n", msg);
}
}
}

2.3 队列操作函数#

/* 初始化 */
void chQueueInit(queue_t *qp, msg_t *buf, cnt_t n);
/* 发送消息 */
msg_t chQueueSend(queue_t *qp, const msg_t *msgp);
msg_t chQueueSendTimeout(queue_t *qp, const msg_t *msgp, sysinterval_t timeout);
msg_t chQueueSendI(queue_t *qp, const msg_t *msgp);
/* 接收消息 */
msg_t chQueueReceive(queue_t *qp, msg_t *msgp);
msg_t chQueueReceiveTimeout(queue_t *qp, msg_t *msgp, sysinterval_t timeout);
msg_t chQueueReceiveI(queue_t *qp, msg_t *msgp);
/* 向前看(不移除) */
msg_t chQueuePeek(queue_t *qp, msg_t *msgp);
/* 获取当前消息数量 */
cnt_t chQueueGetCountI(queue_t *qp);

三、对象队列(Objects Queue)#

对象队列与普通队列类似,但它传递的是对象指针而非消息值。这使得对象队列非常适合管理对象池,避免了数据拷贝的开销。

3.1 数据结构#

typedef struct {
void **buf; /* 对象指针缓冲区 */
cnt_t cnt; /* 缓冲区大小 */
cnt_t wrptr; /* 写指针 */
cnt_t rdptr; /* 读指针 */
cnt_t n; /* 当前对象数量 */
/* ... 内部等待队列 ... */
} objects_queue_t;

3.2 初始化与基本操作#

#define OBJ_QUEUE_SIZE 8
static void *obj_queue_buffer[OBJ_QUEUE_SIZE];
static objects_queue_t obj_queue;
/* 假设有一个自定义结构体 */
typedef struct {
int id;
char data[32];
} my_object_t;
static my_object_t objects[OBJ_QUEUE_SIZE];
void obj_queue_example_init(void) {
/* 初始化对象队列 */
chObjectsQueueInit(&obj_queue, obj_queue_buffer, OBJ_QUEUE_SIZE);
/* 将预分配的对象放入队列 */
for (int i = 0; i < OBJ_QUEUE_SIZE; i++) {
objects[i].id = i;
chObjectsQueuePost(&obj_queue, &objects[i]);
}
}
/* 生产者:发布对象 */
static THD_WORKING_AREA(wa_obj_producer, 256);
static THD_FUNCTION(obj_producer_thread, arg) {
(void)arg;
while (true) {
my_object_t *obj;
msg_t result = chObjectsQueueFetchTimeout(&obj_queue, (void **)&obj, TIME_MS2I(200));
if (result == MSG_OK) {
/* 使用对象 */
obj->id++;
chprintf((BaseSequentialStream *)&SD1, "Object %d processed\r\n", obj->id);
/* 归还对象 */
chObjectsQueuePost(&obj_queue, obj);
}
chThdSleepMilliseconds(100);
}
}

3.3 对象队列操作函数#

/* 初始化 */
void chObjectsQueueInit(objects_queue_t *oqp, void **buf, cnt_t n);
/* 发布对象 */
msg_t chObjectsQueuePost(objects_queue_t *oqp, void *objp);
msg_t chObjectsQueuePostTimeout(objects_queue_t *oqp, void *objp, sysinterval_t timeout);
msg_t chObjectsQueuePostI(objects_queue_t *oqp, void *objp);
/* 获取对象 */
msg_t chObjectsQueueFetch(objects_queue_t *oqp, void **objp);
msg_t chObjectsQueueFetchTimeout(objects_queue_t *oqp, void **objp, sysinterval_t timeout);
msg_t chObjectsQueueFetchI(objects_queue_t *oqp, void **objp);
/* 向前看 */
msg_t chObjectsQueuePeek(objects_queue_t *oqp, void **objp);

四、对象FIFO(Objects FIFO)#

对象FIFO是ChibiOS/RT中性能最高的IPC机制之一。它专为传递固定大小的对象而设计,内部使用环形缓冲区,支持零拷贝操作。

4.1 数据结构#

typedef struct {
uint8_t *buf; /* 内部缓冲区 */
size_t objsize; /* 单个对象大小 */
cnt_t cnt; /* 对象容量 */
cnt_t wrptr; /* 写指针 */
cnt_t rdptr; /* 读指针 */
cnt_t n; /* 当前对象数量 */
/* ... 内部等待队列 ... */
} objects_fifo_t;

4.2 初始化与基本操作#

#define FIFO_OBJ_SIZE sizeof(sensor_data_t)
#define FIFO_OBJ_COUNT 16
/* 定义FIFO结构和缓冲区 */
static objects_fifo_t sensor_fifo;
static uint8_t sensor_fifo_buffer[FIFO_OBJ_COUNT * FIFO_OBJ_SIZE];
/* 传感器数据结构 */
typedef struct {
uint32_t timestamp;
int16_t temperature;
uint16_t humidity;
int32_t pressure;
} sensor_data_t;
void fifo_example_init(void) {
/* 初始化对象FIFO */
chObjectsFifoObjectInit(&sensor_fifo, sensor_fifo_buffer,
FIFO_OBJ_COUNT, FIFO_OBJ_SIZE);
}
/* 传感器线程:发布数据 */
static THD_WORKING_AREA(wa_sensor, 512);
static THD_FUNCTION(sensor_thread, arg) {
(void)arg;
while (true) {
sensor_data_t data;
data.timestamp = chVTGetSystemTimeX();
data.temperature = read_temperature();
data.humidity = read_humidity();
data.pressure = read_pressure();
/* 发布数据到FIFO */
msg_t result = chObjectsFifoPostTimeout(&sensor_fifo, &data, TIME_MS2I(50));
if (result != MSG_OK) {
/* FIFO已满,数据丢失 */
}
chThdSleepMilliseconds(1000);
}
}
/* 数据处理线程:获取数据 */
static THD_WORKING_AREA(wa_processor, 512);
static THD_FUNCTION(processor_thread, arg) {
(void)arg;
sensor_data_t data;
while (true) {
/* 从FIFO获取数据 */
msg_t result = chObjectsFifoFetchTimeout(&sensor_fifo, &data, TIME_INFINITE);
if (result == MSG_OK) {
process_sensor_data(&data);
}
}
}

4.3 对象FIFO操作函数#

/* 初始化 */
void chObjectsFifoObjectInit(objects_fifo_t *ofp, uint8_t *buf,
cnt_t cnt, size_t objsize);
/* 发布对象(会拷贝数据) */
msg_t chObjectsFifoPost(objects_fifo_t *ofp, const void *objp);
msg_t chObjectsFifoPostTimeout(objects_fifo_t *ofp, const void *objp, sysinterval_t timeout);
msg_t chObjectsFifoPostI(objects_fifo_t *ofp, const void *objp);
/* 获取对象(会拷贝数据) */
msg_t chObjectsFifoFetch(objects_fifo_t *ofp, void *objp);
msg_t chObjectsFifoFetchTimeout(objects_fifo_t *ofp, void *objp, sysinterval_t timeout);
msg_t chObjectsFifoFetchI(objects_fifo_t *ofp, void *objp);

五、对象缓存(Object Cache)#

对象缓存提供了一种高效的对象复用机制,支持LRU(最近最少使用)淘汰策略和Hash查找。它适用于需要频繁分配和释放固定类型对象的场景。

5.1 数据结构#

typedef struct {
void **cache; /* 缓存对象指针数组 */
cnt_t size; /* 缓存容量 */
cnt_t count; /* 当前缓存对象数量 */
/* LRU相关字段 */
/* Hash相关字段 */
} obj_cache_t;

5.2 初始化与基本操作#

#define CACHE_SIZE 32
static obj_cache_t buffer_cache;
static void *cache_buffer[CACHE_SIZE];
/* 缓冲区对象 */
typedef struct {
size_t size;
uint8_t data[256];
} buffer_object_t;
static buffer_object_t buffer_objects[CACHE_SIZE];
void cache_example_init(void) {
/* 初始化对象缓存 */
chObjCacheObjectInit(&buffer_cache, cache_buffer, CACHE_SIZE);
/* 预填充缓存 */
for (int i = 0; i < CACHE_SIZE; i++) {
chObjCacheFree(&buffer_cache, &buffer_objects[i]);
}
}
/* 分配对象 */
static THD_WORKING_AREA(wa_cache_user, 512);
static THD_FUNCTION(cache_user_thread, arg) {
(void)arg;
while (true) {
buffer_object_t *obj;
/* 从缓存分配对象 */
msg_t result = chObjCacheAlloc(&buffer_cache, (void **)&obj);
if (result == MSG_OK) {
/* 使用对象 */
process_buffer(obj);
/* 释放对象回缓存 */
chObjCacheFree(&buffer_cache, obj);
} else {
/* 缓存为空,需要动态分配 */
obj = chHeapAlloc(NULL, sizeof(buffer_object_t));
if (obj != NULL) {
process_buffer(obj);
chHeapFree(obj);
}
}
chThdSleepMilliseconds(50);
}
}

5.3 对象缓存操作函数#

/* 初始化 */
void chObjCacheObjectInit(obj_cache_t *cp, void **buf, cnt_t size);
/* 分配对象 */
msg_t chObjCacheAlloc(obj_cache_t *cp, void **objp);
msg_t chObjCacheAllocI(obj_cache_t *cp, void **objp);
/* 释放对象 */
msg_t chObjCacheFree(obj_cache_t *cp, void *objp);
msg_t chObjCacheFreeI(obj_cache_t *cp, void *objp);
/* 获取缓存状态 */
cnt_t chObjCacheGetCountI(obj_cache_t *cp);

六、动态对象工厂(Factory)#

动态对象工厂提供了一种管理动态对象生命周期的机制。它允许注册、创建和回收对象,支持对象的引用计数和延迟释放。

6.1 数据结构#

typedef struct {
void *obj; /* 对象指针 */
sysinterval_t lifetime; /* 对象生命周期 */
/* ... 其他管理字段 ... */
} factory_object_t;
typedef struct {
factory_object_t *objects; /* 对象数组 */
cnt_t cnt; /* 对象数量 */
/* ... 内部管理字段 ... */
} factory_t;

6.2 初始化与基本操作#

#define FACTORY_MAX_OBJECTS 16
static factory_t my_factory;
static factory_object_t factory_objects[FACTORY_MAX_OBJECTS];
/* 可创建的对象类型 */
typedef struct {
int type;
void *data;
size_t size;
} managed_object_t;
static managed_object_t managed_objects[FACTORY_MAX_OBJECTS];
void factory_example_init(void) {
/* 初始化工厂 */
chFactoryObjectInit(&my_factory, factory_objects, FACTORY_MAX_OBJECTS);
/* 注册对象到工厂 */
for (int i = 0; i < FACTORY_MAX_OBJECTS; i++) {
managed_objects[i].type = 0;
managed_objects[i].data = NULL;
managed_objects[i].size = 0;
chFactoryRegisterObject(&my_factory, &factory_objects[i],
&managed_objects[i]);
}
}
/* 工作线程:获取和使用对象 */
static THD_WORKING_AREA(wa_factory_user, 512);
static THD_FUNCTION(factory_user_thread, arg) {
(void)arg;
while (true) {
factory_object_t *fobj;
managed_object_t *mobj;
/* 从工厂获取对象 */
msg_t result = chFactoryTakeObject(&my_factory, &fobj,
TIME_MS2I(100));
if (result == MSG_OK) {
/* 获取关联的托管对象 */
mobj = (managed_object_t *)chFactoryGetUserData(fobj);
/* 使用对象 */
mobj->type = 1;
process_object(mobj);
/* 归还对象到工厂 */
chFactoryReleaseObject(&my_factory, fobj);
}
chThdSleepMilliseconds(200);
}
}

6.3 工厂操作函数#

/* 初始化 */
void chFactoryObjectInit(factory_t *fp, factory_object_t *objs, cnt_t cnt);
/* 注册对象 */
msg_t chFactoryRegisterObject(factory_t *fp, factory_object_t *fobj, void *data);
msg_t chFactoryRegisterObjectI(factory_t *fp, factory_object_t *fobj, void *data);
/* 获取对象 */
msg_t chFactoryTakeObject(factory_t *fp, factory_object_t **fobjp, sysinterval_t timeout);
msg_t chFactoryTakeObjectI(factory_t *fp, factory_object_t **fobjp);
/* 释放对象 */
msg_t chFactoryReleaseObject(factory_t *fp, factory_object_t *fobj);
msg_t chFactoryReleaseObjectI(factory_t *fp, factory_object_t *fobj);
/* 获取用户数据 */
void *chFactoryGetUserData(factory_object_t *fobj);

七、IPC机制选择指南#

在实际项目中,选择合适的IPC机制需要考虑多个因素:

graph TB A[选择IPC机制] --> B{需要传递什么?} B -->|简单消息| C[队列 Queue] B -->|对象指针| D{对象大小是否固定?} B -->|固定大小数据| E[对象FIFO] D -->|是| E D -->|否| F{需要缓存复用?} F -->|是| G[对象缓存] F -->|否| H[对象队列] C --> I[轻量级, 适合简单通信] E --> J[高性能, 零拷贝] G --> K[高效复用, LRU淘汰] H --> L[灵活指针传递] I --> M[控制命令, 状态通知] J --> N[传感器数据, 音频流] K --> O[缓冲区管理, 网络包] L --> P[异步任务分发]

选择建议#

场景推荐机制理由
线程间简单命令传递队列最轻量,开销最小
传感器数据采集对象FIFO固定大小结构,高性能
网络包缓冲池对象缓存LRU淘汰,高效复用
异步任务分发对象队列灵活传递任意对象指针
复杂对象生命周期管理动态工厂支持引用计数和延迟释放
音频流处理对象FIFO零拷贝,确定性延迟
日志缓冲队列简单可靠,FIFO顺序

性能对比#

/* 性能测试示例:队列 vs 对象FIFO */
#include "ch.h"
#include "oslib.h"
#define TEST_ITERATIONS 10000
static queue_t test_queue;
static msg_t test_queue_buffer[64];
static objects_fifo_t test_fifo;
static uint8_t test_fifo_buffer[64 * sizeof(large_data_t)];
typedef struct {
uint32_t data[16]; /* 64字节数据 */
} large_data_t;
void performance_test(void) {
systime_t start, end;
/* 测试队列性能 */
chQueueInit(&test_queue, test_queue_buffer, 64);
start = chVTGetSystemTimeX();
for (int i = 0; i < TEST_ITERATIONS; i++) {
msg_t msg = i;
chQueueSend(&test_queue, &msg);
chQueueReceive(&test_queue, &msg);
}
end = chVTGetSystemTimeX();
chprintf((BaseSequentialStream *)&SD1, "Queue: %d ticks\r\n",
(int)(end - start));
/* 测试对象FIFO性能 */
chObjectsFifoObjectInit(&test_fifo, test_fifo_buffer, 64, sizeof(large_data_t));
large_data_t data = {0};
start = chVTGetSystemTimeX();
for (int i = 0; i < TEST_ITERATIONS; i++) {
chObjectsFifoPost(&test_fifo, &data);
chObjectsFifoFetch(&test_fifo, &data);
}
end = chVTGetSystemTimeX();
chprintf((BaseSequentialStream *)&SD1, "Objects FIFO: %d ticks\r\n",
(int)(end - start));
}

八、总结#

ChibiOS/RT的OS库提供了丰富且灵活的IPC机制,每种机制都有其特定的适用场景。在实际开发中,应根据数据类型、性能需求、内存限制等因素综合考虑,选择最合适的IPC机制。

  • 队列:最基础的IPC,适合简单消息传递
  • 对象队列:传递对象指针,适合对象池管理
  • 对象FIFO:高性能固定大小对象传递,适合传感器数据等场景
  • 对象缓存:提供LRU淘汰策略,适合缓冲区管理
  • 动态工厂:完整的对象生命周期管理,适合复杂应用场景

合理使用这些IPC机制,可以构建出高效、可靠的嵌入式多线程系统。

ChibiOS/RT OS库与IPC机制
/posts/chibios-rt-oslib/
作者
JJZBQA
发布于
2024-12-21
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时