打印
[其他]

裸机中断与主程序通信中“真正致命”的BUG示例

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

裸机中断与主程序通信中“真正致命”的BUG示例

【BUG 1】共享变量非原子操作,数据撕裂(数据一半新一半旧)场景:
MCU 32位,操作一个64位数据,裸机环境。
c
复制编辑


volatile uint64_t shared_value = 0;

void ISR_Handler(void) {
    shared_value = 0x1122334455667788;
}

void main(void) {
    while (1) {
        uint64_t temp = shared_value;
        printf("%08X %08X\n", (uint32_t)(temp >> 32), (uint32_t)(temp & 0xFFFFFFFF));
    }
}


致命点:
  • shared_value 是64位,STM32F1/F4是32位总线,CPU拆成两次访问:

    • 先读取高32位
    • 然后低32位

  • 假设中断恰好在两次访问之间发生,中断更新了shared_value,结果:

txt
复制编辑


高32位 = 0x11223344   (旧数据)低32位 = 0x55667788   (新数据)

  • 打印输出数据撕裂,完全错误,数据不可信。


根源:
  • 多次非原子性访问
  • 中断抢占破坏数据一致性


解决:
  • 禁用中断包裹整个读取过程:

c
复制编辑


__disable_irq();
uint64_t temp = shared_value;
__enable_irq();

✅ 这样,确保整个64位数据在读取过程中不中断,数据完整。



【BUG 2】标志位判断顺序错误,数据丢失场景:
中断采集数据,主程序轮询处理:
c
复制编辑


volatile int data_ready = 0;
volatile int data = 0;

void ISR_Handler(void) {
    data = read_sensor();
    data_ready = 1;
}

void main(void) {
    while (1) {
        if (data_ready) {
            process(data);
            data_ready = 0;
        }
    }
}



致命点:
  • data_ready 设为1后,主程序尚未判断,中断又触发:

    • data = 新数据2
    • data_ready = 1

  • 主程序此时:

    • 判断data_ready为1
    • 处理数据,其实处理的是新数据2

  • 结果

    • 新数据1完全丢失
    • 只有最新数据保留,导致数据遗漏,尤其对ADC、传感器连续采集极其致命



解决:
  • 使用缓冲区,避免数据覆盖:

c
复制编辑


#define BUF_SIZE 4
volatile int buffer[BUF_SIZE];
volatile int head = 0;
volatile int tail = 0;

void ISR_Handler(void) {
    buffer[head] = read_sensor();
    head = (head + 1) % BUF_SIZE;
}

void main(void) {
    while (1) {
        if (tail != head) {
            process(buffer[tail]);
            tail = (tail + 1) % BUF_SIZE;
        }
    }
}


✅ 数据多次采样绝不丢失,先进先出。


【BUG 3】编译器优化假死,变量未加volatile场景:
中断置标志,主程序等待:
c
复制编辑


int flag = 0;

void ISR_Handler(void) {
    flag = 1;
}

void main(void) {
    while (1) {
        if (flag) {
            flag = 0;
            do_something();
        }
    }
}



致命点:
  • 未加volatile,编译器优化:

c
复制编辑


if (flag) {   // 可能编译器提前缓存flag,循环内只读一次}

  • 中断虽置位,主程序却看不到新值
  • 程序假死,do_something()***不执行


解决:c
复制编辑


volatile int flag = 0;

✅ 告诉编译器,变量随时可能变,强制每次重新读内存。


【BUG 4】中断滥用禁用,导致丢失其他重要中断场景:c
复制编辑


void ISR1(void) {
    __disable_irq();
    delay_ms(10);  // 假设长时间操作
    __enable_irq();
}



致命点:
  • 在中断内禁用全局中断
  • 如果其他中断(如串口接收、定时器)在这期间触发,全部丢失
  • 系统无法实时响应,严重影响可靠性


正确思路:
  • 避免中断内长时间禁用中断
  • 中断快速退出,必要保护时,在主程序使用:

c
复制编辑


__disable_irq();
critical_data = new_value;
__enable_irq();


✅ 保证重要中断不被滥用禁用影响。


【BUG 5】共享结构体多字段不一致场景:c
复制编辑


typedef struct {
    int x;
    int y;
} Point;

volatile Point shared_point = {0, 0};

void ISR_Handler(void) {
    shared_point.x = get_x();
    shared_point.y = get_y();
}



致命点:
  • 中断在两次赋值之间,主程序读取,得到:

c
复制编辑


x = 新值,y = 旧值

  • 结构体整体失效,数据不匹配


解决:
  • 禁用中断包裹整体结构体读写:

c
复制编辑


__disable_irq();
Point temp = shared_point;
__enable_irq();


✅ 确保结构体数据整体一致。


终极总结:真正致命BUG分类
[td]
错误类型具体表现后果正确做法
非原子多次访问数据撕裂错误数据,难以排查禁用中断确保整体读写
标志位顺序错误覆盖未处理数据,数据丢失重要数据漏掉使用缓冲区
缓存优化假死主程序读不到中断修改的数据系统逻辑假死变量加volatile
中断滥用禁用中断内长时间禁用全局中断丢失重要中断,系统卡死简化中断逻辑,主程序做耗时操作
结构体不一致多字段更新不同步,数据逻辑混乱结构体整体失效保护整体操作



使用特权

评论回复

相关帖子

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

本版积分规则

11

主题

29

帖子

1

粉丝