共享数据的竞争问题
- 问题:保护中断与主程序共享的avg_data
- 方法一:使用关中断保护
- 1. 添加关中断宏
- 2. 修改数据读取代码
- 3. 修改中断服务程序(ISR)
- 方法二:使用原子操作(需平台支持)
- 1. 定义原子类型
- 2. 修改数据访问代码
- 方法三:使用RTOS同步机制(如信号量)
- 1. 扩展通道结构体
- 2. 初始化信号量
- 3. 保护数据访问
- 方案对比与选型建议
- 关键验证点
问题:保护中断与主程序共享的avg_data
在嵌入式系统中,中断服务程序(ISR)和主程序(或任务)共享数据时,必须确保访问的原子性和可见性。以下是针对该问题的具体修改步骤:
方法一:使用关中断保护
此方法通过临时禁用中断来确保对共享变量的原子访问,适用于所有平台。
1. 添加关中断宏
在头文件中定义关中断宏:
2. 修改数据读取代码
在 hal_get_data
函数中保护 avg_data
的读取:
int hal_get_data() {ENTER_CRITICAL(); // 进入临界区(关中断)*val = avg_data; // 安全读取共享数据EXIT_CRITICAL(); // 退出临界区(开中断)return 0;
}
3. 修改中断服务程序(ISR)
在 module_isr
中保护 avg_data
的写入:
irqreturn_t module_isr() {ENTER_CRITICAL();avg_data = 8888;// 安全更新 avg_dataEXIT_CRITICAL();sem_give(complete);}// ...
}
注意这个做法是有风险的,在中断处理函数中关中断需谨慎,可能破坏中断上下文的状态或影响实时性详细分析见博客
方法二:使用原子操作(需平台支持)
如果目标平台支持对 u16
类型的原子访问(如ARM Cortex-M),可使用原子操作。
1. 定义原子类型
在结构体中声明 avg_data
为原子变量:
#include <stdatomic.h>struct siliumixi {// ..._Atomic u16 avg_data; // C11原子类型// ...
};
2. 修改数据访问代码
-
ISR中原子写入:
atomic_store(&chan->avg_data, calculated_value);
-
主程序原子读取:
*val = atomic_load(&chan->avg_data); // 原子读取
方法三:使用RTOS同步机制(如信号量)
在RTOS环境中,可通过信号量确保数据访问的同步。
1. 扩展通道结构体
struct siliumixi {// ...sem_t data_sem; // 新增信号量// ...
};
2. 初始化信号量
在初始化时创建信号量:
chan->data_sem = sem_create(1); // 初始值为1(二进制信号量)
}
3. 保护数据访问
-
ISR中释放信号量:
sem_take(chan->data_sem, 0); // 获取信号量(非阻塞)chan->avg_data = 8888; // 更新数据aicos_sem_give(chan->data_sem); // 释放信号量}// ... }
-
主程序获取信号量:
if (sem_take(chan->data_sem, timeout) != 0)return -ETIMEDOUT;*val = chan->avg_data;}
方案对比与选型建议
方案 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
关中断 | 简单高效,无额外资源消耗 | 影响中断响应实时性 | 低延迟、单核嵌入式系统 |
原子操作 | 代码简洁,无锁竞争 | 依赖平台原子操作支持 | 支持C11原子类型的平台 |
RTOS信号量 | 兼容多任务环境,灵活 | 增加RTOS开销,需处理信号量超时 | 基于RTOS的多任务系统 |
关键验证点
- 原子性测试:在高低优先级任务/中断同时访问时,数据是否一致。
- 性能测试:关中断时长是否影响其他中断响应(使用示波器测量最坏关中断时间)。
- 资源占用:信号量或原子操作是否增加内存/CPU开销。
通过上述任一方法,可有效解决共享数据 chan->avg_data
的竞争问题