前言: 在《抛弃 Malloc》一文中,我们设计了一个静态对象池:
Motor_Handle Motor_Create(void) {
for (int i = 0; i < MAX; i++) {
if (pool[i].is_allocated == 0) { // A
pool[i].is_allocated = 1; // B
return i;
}
}
}
这段代码在裸机(单线程)环境下是完美的。 但在 RTOS(多任务)环境下,它就是一个 “定时炸弹”。
假设 任务 A 刚执行完语句 A(判断未占用),还没来得及执行 B(标记占用),就被高优先级的 任务 B 打断了。 任务 B 也执行了 Motor_Create,它发现该位置也是空的,于是占用了它,拿到了句柄 0。 任务 B 执行完,切回任务 A。任务 A 继续执行语句 B,也占用了位置 0,拿到了句柄 0。
后果:两个任务拿到了同一个句柄,操作同一个内存块,系统逻辑彻底错乱。
这就是经典的 竞态条件 (Race Condition)。
一、 保护资源分配:临界区 (Critical Section)
我们要保护的第一个地方,是 “分配资源” 的过程。 在扫描池子的时候,绝对不允许任何人打断。
1.1 改造 Create 函数
我们需要引入 RTOS 的 临界区保护(关中断或调度锁)。
// motor_driver.c
Motor_Handle Motor_Create(void) {
Motor_Handle h = MOTOR_INVALID_HANDLE;
// 【加锁】进入临界区 (关中断)
// 不同的 RTOS 写法不同,这里用伪代码
OS_ENTER_CRITICAL();
for (int i = 0; i < MAX_MOTORS; i++) {
if (motor_pool[i].is_allocated == 0) {
motor_pool[i].is_allocated = 1;
h = (Motor_Handle)i;
break; // 找到了,赶紧退出循环
}
}
// 【解锁】退出临界区 (开中断)
OS_EXIT_CRITICAL();
return h;
}
加上这两行代码,你的静态内存池就变成了 “线程安全 (Thread-Safe)” 的了。
二、 保护内部数据:对象锁 (Mutex)
仅仅保护分配是不够的。 假设任务 A 正在调用 Motor_SetSpeed 修改参数,刚改了一半(比如改了频率,还没改占空比),任务 B 插进来读取了状态。这时候任务 B 读到的就是 脏数据。
我们需要给 每一个对象 配一把锁。
2.1 在结构体中引入互斥锁
// motor_driver.c (内部定义)
struct Motor_t {
uint8_t is_allocated;
// ... 业务数据 ...
// 【新增】每个对象一把互斥锁
OS_Mutex_t lock;
};
2.2 在 Create 时初始化锁
Motor_Handle Motor_Create(void) {
// ... 分配逻辑 ...
if (found) {
// 创建互斥锁
OS_Mutex_Create(&motor_pool[i].lock);
return i;
}
}
2.3 在 API 中自动加锁
这是架构设计的精髓:不要让用户去加锁,驱动要在内部自动处理。
int Motor_SetSpeed(Motor_Handle h, uint8_t speed) {
// 1. 校验句柄
if (h >= MAX_MOTORS || !motor_pool[h].is_allocated) return ERROR;
struct Motor_t *p = &motor_pool[h];
// 2. 【自动加锁】等待拿到锁,防止别人同时修改
OS_Mutex_Take(p->lock, TIMEOUT_FOREVER);
// 3. 修改数据 (临界区)
p->current_speed = speed;
HAL_PWM_Set(p->port, speed);
// 4. 【自动解锁】
OS_Mutex_Give(p->lock);
return SUCCESS;
}
三、 总结
通过引入 全局临界区(保护池子)和 对象互斥锁(保护个例),我们将一个“裸机驱动”升级为了 “工业级 RTOS 驱动”。
-
原则 1:资源分配(Create/Destroy)必须原子化。
-
原则 2:对象状态读写必须互斥。
/*******************************************
* Description:
* 本文为作者《嵌入式开发基础与工程实践》系列文之一。
* 关注我即可订阅后续内容更新,采用异步推送机制。
* 转发本文可视为广播分发,有助于信息传播至更多节点。
*******************************************/
1324

被折叠的 条评论
为什么被折叠?



