Skip to content

[Bug] rt_schedule中断屏蔽范围没有包括获取的curr_thread,导致调度异常 #10714

@wdfk-prog

Description

@wdfk-prog

RT-Thread Version

5.1.0~master

Hardware Type/Architectures

ARM M4/STM32

Develop Toolchain

GCC

Describe the bug

复现方式

  1. 实现一个延时函数,使用 nop 指令进行延时。用于插入rt_schedule函数的curr_thread获取到禁用中断之间插入.

    • 更好的将中断触发插入到rt_schedule函数的curr_thread获取到禁用中断之间.
    /**
    * @brief  使用 nop 指令进行延时
    * @param  nop_counts: 需要执行的 nop 指令数量
    * @note   这是一个忙等待延时,会独占 CPU。
    *         延时时间取决于 CPU 主频和编译器的优化行为。
    *         主要用于需要精确指令周期的短延时场景。
    */
    void delay_nops(volatile uint32_t nop_counts)
    {
        while(nop_counts--)
        {
            __asm__ volatile ("nop");
        }
    }
  2. rt_schedule函数中插入delay_nops,模拟将中断触发插入到rt_schedule函数的curr_thread获取到禁用中断之间.

    volatile uint32_t ti = 1;
    /**
    * @brief Perform thread scheduling once. Select the highest priority thread and switch to it.
    *
    * @details This function:
    *   - Disables interrupts to prevent preemption during scheduling
    *   - Checks if scheduler is enabled (lock_nest == 0)
    *   - Gets the highest priority ready thread
    *   - Determines if current thread should continue running or be preempted
    *   - Performs context switch if needed:
    *     * From current thread to new thread (normal case)
    *     * Handles special cases like interrupt context switches
    *   - Manages thread states (READY/RUNNING) and priority queues
    *   - Handles thread yield flags and signal processing
    */
    void rt_schedule(void)
    {
        rt_base_t level;
        struct rt_thread *to_thread;
        struct rt_thread *from_thread;
        /* using local variable to avoid unecessary function call */
        struct rt_thread *curr_thread = rt_thread_self();
        /* 修改开始部分 */
        extern void delay_nops(volatile uint32_t nop_counts);
        delay_nops(ti);
        //使用如下判断来识别是否已经异常
        if(curr_thread != rt_thread_self())
        {
            while (1);
        }
        /* 修改结束部分 */
        /* disable interrupt */
        level = rt_hw_interrupt_disable();
        //后续相同.....
    }
  3. 开启系统软件定时器线程与CAN驱动

    • (can实现与文档例程一致既可)
    • 主要是需要线程阻塞等待唤醒,中断里执行唤醒操作,调用rt_schedule.
    • 即线程中使用(rt_sem_take(&rx_sem, RT_WAITING_FOREVER);)
    • 中断里使用 rt_sem_release(&rx_sem);
    • 并且需要开启两个来模拟
  4. 接入仿真器,执行debug,模拟中断触发在rt_schedule函数的curr_thread获取到禁用中断之间.

    1. 执行完rtt初始化进入main函数后
    2. 添加触发点rt_sem_release(&_soft_timer_sem);
Image
3. 等待debeug暂停,暂停后,设置全局变量`ti`为10000(更好触发中断在`rt_schedule`函数的`curr_thread`获取到禁用中断之间).
4. 在`delay_nops(ti);`加入断点
Image
5. 运行程序到`delay_nops(ti);`断点处,debug暂停
6. 使用can分析仪触发can接收中断
7. 运行程序,已经进入while(1)
Image
  1. rtconfig.h配置如下
#define RT_NAME_MAX 18
#define RT_CPUS_NR 1
#define RT_ALIGN_SIZE 8
#define RT_THREAD_PRIORITY_32
#define RT_THREAD_PRIORITY_MAX 32
#define RT_TICK_PER_SECOND 1000
#define RT_USING_OVERFLOW_CHECK
#define RT_USING_HOOK
#define RT_HOOK_USING_FUNC_PTR
#define RT_USING_IDLE_HOOK
#define RT_IDLE_HOOK_LIST_SIZE 4
#define IDLE_THREAD_STACK_SIZE 256
#define RT_USING_TIMER_SOFT
#define RT_TIMER_THREAD_PRIO 4
#define RT_TIMER_THREAD_STACK_SIZE 512
#define RT_USING_CPU_USAGE_TRACER

#define RT_USING_CAN
#define RT_CAN_USING_HDR
#define RT_CANMSG_BOX_SZ 16
#define RT_CANSND_BOX_NUM 3
#define RT_CANSND_MSG_TIMEOUT 100

使用环境

  1. rtt studio
  2. gcc编译器
  3. master主线代码
  4. stm32f407vg, 主频168mhz

总结

  • struct rt_thread *curr_thread = rt_thread_self();这个需要在中断屏蔽的范围内获取,否则会出现异常
  • 异常情况与中断插入有关.
  • 需要一次执行调度的时候,插入了中断在在rt_schedule函数的curr_thread获取到禁用中断之间.并且这次调度要产生执行线程切换的逻辑,将当前current线程进行修改
  • 当中断执行完毕后再来执行原先的调度逻辑就会产生问题.
  • 问题只是获取的current线程与死机不符.会不会实际造成影响也是概率性问题.
  • 中断要恰好在在rt_schedule函数的curr_thread获取到禁用中断之间触发,也是个概率性问题.只有高频率的触发中断长时间挂测才能不在debug中产生

Other additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions