背景:在系统性的熟悉了RTOS的应用后,下一步需要熟悉RTOS底层实现原理,以更好的将OS移植到不同平台以及在OS任务出现故障的时候更快捷的排查。本文详细描述了RTOS的上下文切换的底层实现原理。
上下文的定义
上下文,指的是在系统运行过程中,当需要从函数 A 切换到函数 B 时,函数 A 所处的状态称为"上文",函数 B 所处的状态称为"下文"。在 RTOS 中,上下文切换是任务切换过程中频繁发生的关键操作,其核心目的是保存当前任务的运行环境,并快速恢复下一个任务的运行环境,从而实现任务间的平滑切换。
对于 MCU 而言,需要保存的主要上下文包括:
- PC 指针(程序计数器):指示当前程序执行到了哪条指令。
- SP 指针(栈指针):指示当前栈顶的位置。
- R0--R31 寄存器:保存当前任务运行过程中的临时变量和状态。
在进行上下文切换时,首先将这些关键寄存器的值保存到任务初始化时分配的栈空间中,完成上文的保存 ;随后,将下一个待运行任务之前保存的上下文从栈中恢复到对应的寄存器中,完成下文的切换。这一过程确保了任务切换前后处理器状态的完整性与连续性。
切换过程示意图
TaskA 拥有下图所示的上下文:

最开始,将PC指针压入A栈中

然后将R0~R31,SREG压入,SP压到栈顶部

开始从B栈内取SP,R0-R31,PC到MCU中,完成切换

不同RTOS的上下文切换的区别
在不同OS中,上下文切换的底层原理是一样的,都是先将关键寄存器保存进入栈,然后将下一个任务的关键寄存器赋值到当前平台上,以完成切换。
基于 PendSV 的延迟切换:
以 FreeRTOS 为代表。当需要切换任务时(如时间片耗尽或高优先级任务就绪),系统不会立即执行切换,而是触发一个低优先级的 PendSV 异常。这种"延迟切换代理"机制将复杂的上下文保存与恢复操作从紧急的中断服务例程(ISR)中剥离出来,避免了嵌套中断中的不可预测行为,保证了中断响应的确定性。
双模式切换机制
RT-Thread 还提供了一种"无中断切换"机制(如 rt_hw_context_switch_to)。这种机制在关闭中断的情况下,直接在调度器上下文中完成切换,完全避开异常入口/出口的额外开销。它特别适合系统启动时首次运行多任务,以及对中断延迟极度敏感的硬实时工业控制场景
#mermaid-svg-mYmQRqaA0rjH9UVB{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-mYmQRqaA0rjH9UVB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mYmQRqaA0rjH9UVB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mYmQRqaA0rjH9UVB .error-icon{fill:#552222;}#mermaid-svg-mYmQRqaA0rjH9UVB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mYmQRqaA0rjH9UVB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mYmQRqaA0rjH9UVB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mYmQRqaA0rjH9UVB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mYmQRqaA0rjH9UVB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mYmQRqaA0rjH9UVB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mYmQRqaA0rjH9UVB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mYmQRqaA0rjH9UVB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mYmQRqaA0rjH9UVB .marker.cross{stroke:#333333;}#mermaid-svg-mYmQRqaA0rjH9UVB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mYmQRqaA0rjH9UVB p{margin:0;}#mermaid-svg-mYmQRqaA0rjH9UVB .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-mYmQRqaA0rjH9UVB .cluster-label text{fill:#333;}#mermaid-svg-mYmQRqaA0rjH9UVB .cluster-label span{color:#333;}#mermaid-svg-mYmQRqaA0rjH9UVB .cluster-label span p{background-color:transparent;}#mermaid-svg-mYmQRqaA0rjH9UVB .label text,#mermaid-svg-mYmQRqaA0rjH9UVB span{fill:#333;color:#333;}#mermaid-svg-mYmQRqaA0rjH9UVB .node rect,#mermaid-svg-mYmQRqaA0rjH9UVB .node circle,#mermaid-svg-mYmQRqaA0rjH9UVB .node ellipse,#mermaid-svg-mYmQRqaA0rjH9UVB .node polygon,#mermaid-svg-mYmQRqaA0rjH9UVB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mYmQRqaA0rjH9UVB .rough-node .label text,#mermaid-svg-mYmQRqaA0rjH9UVB .node .label text,#mermaid-svg-mYmQRqaA0rjH9UVB .image-shape .label,#mermaid-svg-mYmQRqaA0rjH9UVB .icon-shape .label{text-anchor:middle;}#mermaid-svg-mYmQRqaA0rjH9UVB .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-mYmQRqaA0rjH9UVB .rough-node .label,#mermaid-svg-mYmQRqaA0rjH9UVB .node .label,#mermaid-svg-mYmQRqaA0rjH9UVB .image-shape .label,#mermaid-svg-mYmQRqaA0rjH9UVB .icon-shape .label{text-align:center;}#mermaid-svg-mYmQRqaA0rjH9UVB .node.clickable{cursor:pointer;}#mermaid-svg-mYmQRqaA0rjH9UVB .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-mYmQRqaA0rjH9UVB .arrowheadPath{fill:#333333;}#mermaid-svg-mYmQRqaA0rjH9UVB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-mYmQRqaA0rjH9UVB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-mYmQRqaA0rjH9UVB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mYmQRqaA0rjH9UVB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-mYmQRqaA0rjH9UVB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mYmQRqaA0rjH9UVB .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-mYmQRqaA0rjH9UVB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-mYmQRqaA0rjH9UVB .cluster text{fill:#333;}#mermaid-svg-mYmQRqaA0rjH9UVB .cluster span{color:#333;}#mermaid-svg-mYmQRqaA0rjH9UVB div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-mYmQRqaA0rjH9UVB .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-mYmQRqaA0rjH9UVB rect.text{fill:none;stroke-width:0;}#mermaid-svg-mYmQRqaA0rjH9UVB .icon-shape,#mermaid-svg-mYmQRqaA0rjH9UVB .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mYmQRqaA0rjH9UVB .icon-shape p,#mermaid-svg-mYmQRqaA0rjH9UVB .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-mYmQRqaA0rjH9UVB .icon-shape .label rect,#mermaid-svg-mYmQRqaA0rjH9UVB .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mYmQRqaA0rjH9UVB .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-mYmQRqaA0rjH9UVB .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-mYmQRqaA0rjH9UVB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-mYmQRqaA0rjH9UVB .isr>*{fill:#ffebee!important;stroke:#c62828!important;stroke-width:2px!important;}#mermaid-svg-mYmQRqaA0rjH9UVB .isr span{fill:#ffebee!important;stroke:#c62828!important;stroke-width:2px!important;}#mermaid-svg-mYmQRqaA0rjH9UVB .pendsv>*{fill:#e3f2fd!important;stroke:#1565c0!important;stroke-width:2px!important;}#mermaid-svg-mYmQRqaA0rjH9UVB .pendsv span{fill:#e3f2fd!important;stroke:#1565c0!important;stroke-width:2px!important;}#mermaid-svg-mYmQRqaA0rjH9UVB .task>*{fill:#e8f5e9!important;stroke:#2e7d32!important;stroke-width:2px!important;}#mermaid-svg-mYmQRqaA0rjH9UVB .task span{fill:#e8f5e9!important;stroke:#2e7d32!important;stroke-width:2px!important;}#mermaid-svg-mYmQRqaA0rjH9UVB .sched>*{fill:#fff3e0!important;stroke:#ef6c00!important;stroke-width:2px!important;}#mermaid-svg-mYmQRqaA0rjH9UVB .sched span{fill:#fff3e0!important;stroke:#ef6c00!important;stroke-width:2px!important;} 直接切换机制
调度器触发或系统启动
关闭全局中断
在当前上下文直接保存寄存器
更新当前任务指针
在当前上下文直接恢复寄存器
开启全局中断
跳转到新任务 PC
延迟切换机制 PendSV
是
有
无
中断或时间片到期
需要切换?
设置 PendSV 挂起标志
退出当前 ISR
还有更高优先级中断?
继续处理高优先级中断
硬件自动触发 PendSV
在 PendSV 中保存恢复上下文
切换到新任务
核心区别
延迟切换(PendSV):
采用"先标记、后执行"的策略:先设置 PendSV 挂起标志,待所有高优先级中断处理完毕并退出后,再执行实际的上下文切换。
优点:ISR 执行极快,不会被上下文切换阻塞;高优先级中断能够随时抢占低优先级中断,完美支持中断嵌套。
缺点:多了一次异常入口/出口的硬件开销,通常消耗几十个时钟周期。
直接切换:
直接在关闭全局中断的环境下完成上下文切换,切换完毕后再重新开启中断。
优点:省去了异常入口/出口的额外开销,切换动作更加紧凑;在系统启动阶段或关中断临界区内必须使用此方式。
缺点:若在 ISR 内部直接切换,会阻塞高优先级中断的响应,破坏中断实时性,因此通常仅限于调度器内部或系统启动时调用。
总结与要点
- 上下文切换核心步骤:
- 保存上文:硬件自动压入 R0-R3、LR、PC 等;软件手动压入 R4-R11;最后将当前的 SP 存入旧任务的 TCB。
- 调度决策:RTOS 调度器根据优先级,选出下一个要运行的新任务。
- 切换栈指针:从新任务的 TCB 中读取之前保存的 SP,并更新到硬件的 SP 寄存器。
- 恢复下文并跳转:软件手动弹出 R4-R11;随后触发异常返回机制,硬件自动弹出 PC 等寄存器,CPU 从新任务的 PC 处继续执行。
备注: 寄存器数量(如R0-R31与R0-R11)取决于底层MCU架构而"软硬结合"与"纯手动压栈"的区别,在于是否利用Cortex-M硬件自动压栈特性以换取极致切换性能。
- 两种机制的适用场景 :
- 延迟切换(PendSV):适用于需要频繁响应外部中断的系统(如 FreeRTOS),能保证 ISR 快速进出,高优先级中断可随时抢占,适合对中断确定性要求高的场景。
- 直接切换(关中断切换):适用于系统初始化阶段首次启动多任务、调度器内部快速切换,以及不允许有任何额外硬件开销的硬实时控制场景。