C语言从句柄到对象 (七) —— 给对象加把锁:RTOS 环境下的并发安全

前言: 在《抛弃 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:
* 本文为作者《嵌入式开发基础与工程实践》系列文之一。
* 关注我即可订阅后续内容更新,采用异步推送机制。
* 转发本文可视为广播分发,有助于信息传播至更多节点。
*******************************************/

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值