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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions