调度器运行到main_thread_entry的完整执行链路(RA6M3-HMI-Board)
调度器运行到main_thread_entry的完整执行链路(RA6M3 BSP)
核心结论:调度器启动的本质是「第一次上下文切换」,永远切换到就绪队列中优先级最高的线程;main_thread_entry 不是调度器启动的默认入口,而是 main 线程被调度选中时的C语言执行起点,仅当更高优先级线程(如timer线程)阻塞后,main 线程才会被选中执行。
一、前置条件:调度器启动前的线程就绪化
调度器运行(rt_system_scheduler_start)前,系统已完成关键线程的创建和就绪化,为调度提供“可执行线程池”:
1. main线程的创建与就绪
rt_application_init()(src/components.c)会创建main线程,核心动作:
void rt_application_init(void)
{
rt_thread_t tid;
// 以main_thread_entry为入口创建main线程(优先级10)
tid = rt_thread_create("main", main_thread_entry, RT_NULL,
RT_MAIN_THREAD_STACK_SIZE, RT_MAIN_THREAD_PRIORITY, 20);
RT_ASSERT(tid != RT_NULL);
// 启动线程:将main线程加入就绪队列
rt_thread_startup(tid);
}
rt_thread_create内部调用rt_hw_stack_init,把main_thread_entry作为线程入口写入栈帧(PC寄存器值);rt_thread_startup把main线程状态设为RT_THREAD_READY,并挂载到rt_thread_priority_table[10](优先级10的就绪链表),同时置位rt_thread_ready_priority_group的bit10。
2. timer线程的创建与就绪
软件定时器模块初始化时,会创建timer线程(优先级RT_TIMER_THREAD_PRIO=4),并通过rt_thread_startup()加入就绪队列:
- 挂载到
rt_thread_priority_table[4],置位rt_thread_ready_priority_group的bit4; - 优先级4(数值更小)高于main线程的10,成为就绪队列中“最高优先级线程”。
二、调度器运行:第一次上下文切换(核心步骤)
调度器启动的核心函数是rt_system_scheduler_start(src/scheduler_up.c),本质是“选最高优先级线程 + 切过去执行”,代码逻辑拆解:
void rt_system_scheduler_start(void)
{
struct rt_thread *to_thread;
rt_ubase_t highest_ready_priority;
// 步骤1:从就绪队列选最高优先级线程(RA6M3场景下是timer线程)
to_thread = _scheduler_get_highest_priority_thread(&highest_ready_priority);
// 步骤2:标记该线程为当前CPU的运行线程
rt_cpu_self()->current_thread = to_thread;
// 步骤3:清除临界切换标志,确保调度安全
CLR_CRITICAL_SWITCH_FLAG();
// 步骤4:将线程从就绪队列移除(避免重复调度),状态改为RUNNING
rt_sched_remove_thread(to_thread);
RT_SCHED_CTX(to_thread).stat = RT_THREAD_RUNNING;
// 步骤5:触发上下文切换,跳转到to_thread的执行入口
rt_hw_context_switch_to((rt_uintptr_t)&to_thread->sp);
// 切换后永不返回
}
关键子步骤:_scheduler_get_highest_priority_thread 选线程逻辑
static struct rt_thread* _scheduler_get_highest_priority_thread(rt_ubase_t *highest_prio)
{
rt_ubase_t highest_ready_priority;
// 从就绪优先级位图中找最小编置位bit(RA6M3场景下是bit4)
highest_ready_priority = __rt_ffs(rt_thread_ready_priority_group) - 1;
// 从对应优先级链表中取出第一个线程(timer线程)
struct rt_thread *highest_priority_thread = RT_THREAD_LIST_NODE_ENTRY(
rt_thread_priority_table[highest_ready_priority].next
);
*highest_prio = highest_ready_priority;
return highest_priority_thread;
}
三、上下文切换到线程入口(从汇编到C的执行)
rt_hw_context_switch_to 是汇编实现的核心切换函数(libcpu/arm/cortex-m4/context_gcc.S),负责触发PendSV异常完成切换:
1. 触发PendSV异常
rt_hw_context_switch_to:
LDR r1, =rt_interrupt_to_thread // 保存目标线程sp到全局变量
STR r0, [r1]
LDR r0, =NVIC_INT_CTRL // 触发PendSV异常(最低优先级,确保切换安全)
LDR r1, =NVIC_PENDSVSET
STR r1, [r0]
BX lr
2. PendSV异常处理(第一次切换)
PendSV_Handler:
MRS r0, PRIMASK // 关闭总中断,保护切换过程
CPSID I
...
LDR r0, =rt_interrupt_from_thread
LDR r1, [r0]
CBZ r1, switch_to_thread // 第一次切换:from_thread=0,跳过旧线程现场保存
...
switch_to_thread:
LDR r0, =rt_interrupt_to_thread
LDR r1, [r0]
LDR sp, [r1] // 加载目标线程的栈指针sp
POP {r4-r11} // 恢复通用寄存器
POP {r0-r3} // 恢复参数寄存器
POP {r12}
ADD sp, sp, #4 // 跳过LR(初始栈帧中LR为_thread_exit)
POP {lr} // 恢复LR=线程入口(如timer线程入口)
ADD sp, sp, #4 // 跳过xPSR
CPSIE I // 开启总中断
BX lr // 跳转到线程入口执行
- RA6M3场景下,第一次切换的
to_thread是timer线程,因此BX lr会跳转到timer线程入口; - 此时
main_thread_entry还未执行,仅作为main线程栈帧中保存的“待执行入口”。
四、main_thread_entry的执行时机(timer线程阻塞后)
timer线程执行时的核心逻辑是“检查定时器事件 → 无事件则阻塞”,触发二次调度:
1. timer线程阻塞流程
static void rt_timer_thread_entry(void *parameter)
{
while (1)
{
rt_timer_check(); // 检查是否有到期定时器
if (无到期定时器)
{
// 阻塞timer线程(状态改为SUSPENDED),让出CPU
rt_sem_take(&rt_timer_sem, RT_WAITING_FOREVER);
// 阻塞时自动调用rt_schedule()触发重新调度
}
}
}
2. 重新调度选中main线程
rt_schedule() 会重复“选最高优先级线程”逻辑:
- 此时timer线程已阻塞,就绪队列中仅剩main线程(优先级10);
_scheduler_get_highest_priority_thread选中main线程作为to_thread;- 再次触发PendSV异常,执行上下文切换。
3. 切换到main_thread_entry
本次切换的to_thread是main线程,PendSV异常处理中:
- 从main线程的栈帧中恢复寄存器,
BX lr跳转到栈帧中保存的main_thread_entry; - 最终执行
main_thread_entry的C逻辑:static void main_thread_entry(void *parameter) { RT_UNUSED(parameter); #ifdef RT_USING_COMPONENTS_INIT rt_components_init(); // 初始化RT-Thread组件 #endif main(); // 跳转到用户编写的main函数 }
五、核心链路总结(表格版)
| 阶段 | 核心动作 | 执行线程 | 关键函数/指令 |
|---|---|---|---|
| 调度器启动前 | 创建main/timer线程,加入就绪队列 | 初始化上下文(无调度) | rt_application_init()/rt_thread_startup() |
| 调度器运行 | 选最高优先级(4)的timer线程,触发PendSV | -(切换过程无执行流) | rt_system_scheduler_start()/rt_hw_context_switch_to() |
| 第一次切换后 | 执行timer线程逻辑,无定时器则阻塞 | timer线程 | rt_timer_thread_entry()/rt_sem_take() |
| timer线程阻塞 | 触发重新调度,选优先级10的main线程 | -(切换过程无执行流) | rt_schedule()/_scheduler_get_highest_priority_thread() |
| 第二次切换后 | 从栈帧恢复寄存器,跳转到main_thread_entry | main线程 | PendSV_Handler/BX lr → main_thread_entry() |
六、关键概念对应关系
| 通俗表述 | 实际技术含义 |
|---|---|
| “调度器运行” | rt_system_scheduler_start() 完成“选最高优先级线程 + 第一次上下文切换”,系统进入多线程调度状态 |
| “→ main_thread_entry” | 当main线程成为调度器选中的to_thread时,通过PendSV异常恢复栈帧,CPU跳转到main_thread_entry执行 |
| “优先级决定执行顺序” | _scheduler_get_highest_priority_thread 永远选就绪队列中数值最小的优先级线程,因此timer线程先于main线程执行 |
验证方式(RA6M3 BSP)
- 在
rt_timer_thread_entry和main_thread_entry中分别添加日志:// timer线程入口处 LOG_D("enter timer thread (priority: %d)", RT_TIMER_THREAD_PRIO); // main_thread_entry开头 LOG_D("enter main_thread_entry (priority: %d)", RT_MAIN_THREAD_PRIORITY); - 编译下载后,串口日志会先打印
enter timer thread,再打印enter main_thread_entry; - 若修改
RT_MAIN_THREAD_PRIORITY为3(低于4),则调度器启动后会直接切换到main_thread_entry。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)