打印
[单片机资料]

“全局标志”来实现 ISR 和主循环 对一个全局数组的互斥访问。为什么是不可行的

[复制链接]
60|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
hubeiluhua|  楼主 | 2025-7-8 10:13 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 hubeiluhua 于 2025-7-8 10:21 编辑

你想用一个“全局标志”来实现 ISR 和主循环 对一个全局数组的互斥访问。

✅ 理想设想是这样的:
volatile uint8_t lock = 0;              // 全局互斥锁
volatile uint8_t buffer[10];            // 共享数组

void ISR_Handler(void)
{
    if (lock == 0) {
        lock = 1;
        buffer[0] = 0x55; // 修改数组
        lock = 0;
    }
}

int main(void)
{
    while (1) {
        if (lock == 0) {
            lock = 1;
            buffer[0] = 0xAA;  // 也修改数组
            lock = 0;
        }
    }
}
看起来两个地方都判断 lock == 0 再访问数组,设置为 1 表示“加锁”,用完后再释放。

❌ 实际问题在哪里?为什么这个“全局标志互斥”不可靠?
❗关键点:中断随时可能打断主程序执行,中断不管你是否“加锁”了
❌ 错误执行顺序可能如下:

假设 main() 正在进入临界区:
if (lock == 0) {       // lock = 0,准备进入临界区
    lock = 1;          // 这里准备加锁…
此时,突然发生中断!

ISR 进入执行:
if (lock == 0)         // lock 已经设为 1 了,但 ISR 是异步执行,它可能看到 lock 还没改
{
    lock = 1;          // ISR 也以为自己“安全”,其实错了
    buffer[0] = 0x55;  // 与 main 同时访问 buffer[0]
    lock = 0;
}

中断返回后,main() 继续执行:
buffer[0] = 0xAA;      // 和 ISR 的访问冲突!
lock = 0;
❗最终结果:lock 根本无法阻止中断函数访问内存!
✅ 总结本质问题:
项目                                                                             说明
主函数运行中判断 lock=0                                    还没来得及设为1就被中断
ISR 运行时不受“主循环中的 lock”限制                    因为中断是异步的,根本不会等待主函数“加完锁”
所以 ISR 和 main 同时访问数组                            出现数据竞争、破坏、错乱

✅ 正确做法:使用关中断实现真正的互斥访问
volatile uint8_t buffer[10];

void ISR_Handler(void)
{
    buffer[0] = 0x55;  // 直接写,因为 main 会关中断保护
}

int main(void)
{
    while (1) {
        __disable_irq();         // 关闭中断
        buffer[0] = 0xAA;        // 安全访问
        __enable_irq();          // 恢复中断
    }
}
在 main 中访问共享数据时禁止中断,中断中再怎么激活也不会打断当前访问。这样就是真正的互斥。

❌ 为什么“全局标志互斥锁”行不通?
原因                                                                               解释
中断是异步优先级高                                                       不管主函数有没有“设置锁”,中断随时都能发生
主函数不能阻止 ISR 执行                                               只能靠“关中断”机制
标志变量本身不受保护                                               改它的过程也会被中断打断
判断 lock==0 和 lock=1 不是原子操作                       可能中断正好打断两个操作之间

✅ 什么时候可以用全局标志?
✔ 用于主循环等待中断事件:
volatile uint8_t data_ready = 0;

void USART_IRQHandler(void)
{
    buffer[0] = USART_ReceiveData();
    data_ready = 1;
}

int main(void)
{
    while (1) {
        if (data_ready) {
            __disable_irq();     // 保护临界区
            process(buffer[0]);
            data_ready = 0;
            __enable_irq();
        }
    }
}
✅ 总结
用法                                                      是否可行        说明
主循环用 lock 标志尝试互斥 ISR        ❌ 不行            ISR 是异步的,随时会抢占 main,不受 lock 控制
在主循环访问共享数据时关中断           ✅ 推荐            可以真正阻止 ISR 执行
中断中访问共享数据,无需加锁           ✅ 可行            假设主循环有保护
使用全局标志同步而不是互斥(如 data_ready)        ✅ 可以        适合事件通知、状态切换

如果你有特定的代码片段或共享结构体内容,我可以帮你逐句分析需要不需要加锁,或者怎么用关中断保护临界区。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

11

主题

29

帖子

1

粉丝