1221 字
6 分钟
ChibiOS/RT 同步机制
ChibiOS/RT 提供了丰富的同步原语,涵盖从简单的信号量到复杂的事件驱动机制。本文详细讲解每种同步机制的使用方法和适用场景。
信号量(Semaphore)
信号量是 ChibiOS/RT 中最基础的同步机制,用于线程间的同步和资源计数。
初始化
semaphore_t sem;
chSemInit(&sem, 5); /* 初始计数值为 5 */基本操作
/* 等待信号量(阻塞) */chSemWait(&sem);
/* 发送信号量 */chSemSignal(&sem);
/* 尝试获取信号量(非阻塞) */msg_t result = chSemTryWait(&sem);超时等待
/* 带超时的等待,超时返回 MSG_TIMEOUT */msg_t result = chSemWaitTimeout(&sem, MS2ST(100));if (result == MSG_TIMEOUT) { /* 超时处理 */}二值信号量
初始值为 1 的信号量即为二值信号量,用于互斥访问共享资源:
semaphore_t bin_sem;chSemInit(&bin_sem, 1); /* 二值信号量 */计数信号量
初始值大于 1 的信号量,可用于资源池管理:
#define MAX_RESOURCES 4semaphore_t pool_sem;chSemInit(&pool_sem, MAX_RESOURCES);
void acquire_resource(void) { chSemWait(&pool_sem); /* 申请资源 */ /* 使用资源 */}
void release_resource(void) { chSemSignal(&pool_sem); /* 释放资源 */}互斥锁(Mutex)
互斥锁用于保护临界区,ChibiOS/RT 实现了带优先级继承的递归互斥锁。
基本用法
mutex_t mtx;
chMtxObjectInit(&mtx);
void critical_section(void) { chMtxLock(&mtx); /* 临界区代码 */ chMtxUnlock(&mtx);}递归互斥
同一线程可以多次获取同一把锁:
void nested_lock(void) { chMtxLock(&mtx); /* 第一次获取 */
chMtxLock(&mtx); /* 递归获取成功 */
chMtxUnlock(&mtx); chMtxUnlock(&mtx); /* 两次 unlock 对应两次 lock */}优先级继承
当高优先级线程被低优先级线程持有的互斥锁阻塞时,内核会临时提升低优先级线程的优先级,避免优先级反转问题。
/* 内核自动处理优先级继承,无需额外配置 */static void high_prio_thread(void *arg) { (void)arg; chMtxLock(&mtx); /* 如果锁被低优先级线程持有,会触发优先级继承 */ /* 临界区 */ chMtxUnlock(&mtx);}
static void low_prio_thread(void *arg) { (void)arg; chMtxLock(&mtx); /* 持有锁 */ /* 长时间操作 */ chMtxUnlock(&mtx);}条件变量(Condition Variable)
条件变量用于线程间基于条件的同步等待。
初始化
condvar_t cv;chCondObjectInit(&cv);等待和通知
mutex_t cv_mtx;condvar_t cv;
void producer_thread(void *arg) { (void)arg; chMtxLock(&cv_mtx); /* 准备数据 */ chCondSignal(&cv); /* 唤醒一个等待线程 */ chMtxUnlock(&cv_mtx);}
void consumer_thread(void *arg) { (void)arg; chMtxLock(&cv_mtx); chCondWait(&cv); /* 等待条件满足 */ /* 处理数据 */ chMtxUnlock(&cv_mtx);}广播唤醒
/* chCondBroadcast 唤醒所有等待线程 */void notify_all(void) { chMtxLock(&cv_mtx); chCondBroadcast(&cv); chMtxUnlock(&cv_mtx);}事件(Event)
事件机制允许线程等待一组事件标志的组合。
初始化
event_source_t es;chEvtObjectInit(&es);注册监听
event_listener_t el;
void listener_thread(void *arg) { (void)arg; /* 注册监听,关注 EVT1 和 EVT2 */ chEvtRegister(&es, &el, EVT1 | EVT2);
while (1) { eventmask_t events = chEvtWaitAny(ALL_EVENTS); if (events & EVT1) { /* 处理事件 1 */ } if (events & EVT2) { /* 处理事件 2 */ } }}发送事件
/* 向事件源发送信号 */void send_event(void) { chEvtSignal(&es, EVT1);}广播事件
/* 广播事件给所有监听者 */void broadcast_event(void) { chEvtBroadcast(&es, EVT3);}邮箱(Mailbox)
邮箱是有界的消息队列,用于线程间传递固定大小的消息。
初始化
#define MAILBOX_SIZE 8msg_t mb_buffer[MAILBOX_SIZE];mailbox_t mb;
chMBObjectInit(&mb, mb_buffer, MAILBOX_SIZE);发送和接收
/* 发送消息 */void producer(void) { chMBPost(&mb, 0x1234, MS2ST(50)); /* 带超时 */}
/* 接收消息 */void consumer(void) { msg_t msg; if (chMBFetch(&mb, &msg, TIME_INFINITE) == MSG_OK) { /* 处理消息 msg */ }}非阻塞操作
/* 非阻塞发送 */msg_t result = chMBPostTimeout(&mb, data, TIME_IMMEDIATE);
/* 非阻塞接收 */msg_t result = chMBFetchTimeout(&mb, &msg, TIME_IMMEDIATE);管道(Pipe)
管道提供面向字节流的通信机制,与邮箱不同,管道传输的是字节序列。
初始化
#define PIPE_SIZE 128uint8_t pipe_buffer[PIPE_SIZE];pipe_t pipe;
chPipeObjectInit(&pipe, pipe_buffer, PIPE_SIZE);写入和读取
/* 写入字节流 */void writer(void) { const uint8_t data[] = {0x01, 0x02, 0x03, 0x04}; chPipeWrite(&pipe, data, sizeof(data));}
/* 读取字节流 */void reader(void) { uint8_t buf[32]; size_t n = chPipeRead(&pipe, buf, sizeof(buf)); /* 处理 n 个字节 */}查询可用数据
size_t available = chPipeGetFull(&pipe);if (available >= 10) { /* 有足够数据可读 */}同步机制选择指南
| 机制 | 适用场景 | 特点 |
|---|---|---|
| 信号量 | 资源计数、线程同步 | 轻量、灵活、无优先级继承 |
| 互斥锁 | 保护临界区 | 递归、优先级继承 |
| 条件变量 | 基于条件的等待 | 需配合互斥锁使用 |
| 事件 | 多条件等待、异步通知 | 事件标志位、广播支持 |
| 邮箱 | 固定大小消息传递 | 有界队列、超时控制 |
| 管道 | 字节流通信 | 面向字节、可查询长度 |
选择建议
- 简单资源同步 → 信号量
- 临界区保护 → 互斥锁
- 生产者-消费者 → 邮箱或管道
- 多事件等待 → 事件机制
- 复杂条件同步 → 条件变量 + 互斥锁
总结
ChibiOS/RT 的同步机制覆盖了嵌入式系统中常见的各种同步需求。信号量和互斥锁是使用最广泛的基础原语,事件机制适合复杂的异步场景,邮箱和管道则解决了线程间数据传递的问题。合理选择和组合这些同步原语,是构建可靠实时系统的关键。
部分信息可能已经过时