什么是上下文
上下文是任务执行状态的完整快照,包含 CPU 寄存器、程序计数器、栈指针、状态寄存器等。 在 HRTOS 中,上下文主要通过任务栈来保存和恢复。
在 HRTOS v2 中,上下文切换由调度器触发,用于在 task_A 与 task_B 之间保存与恢复执行状态, 实现 LED 任务的交替运行。上下文切换是 RTOS 实现多任务运行的核心机制。
系统运行两个任务:task_A 与 task_B,每个任务独立控制一个 LED, 并通过延时循环形成可观察的切换效果。验证两个独立任务在同一CPU上通过调度器实现交替执行。
上下文切换是 RTOS 实现多任务运行的核心机制
上下文(Context)是指任务在执行过程中的完整状态,包括程序计数器、寄存器值、栈指针等。 当调度器决定切换任务时,必须保存当前任务的上下文,并恢复目标任务的上下文, 这样才能确保任务从中断点继续执行,实现多任务的并发运行。
上下文是任务执行状态的完整快照,包含 CPU 寄存器、程序计数器、栈指针、状态寄存器等。 在 HRTOS 中,上下文主要通过任务栈来保存和恢复。
多任务系统中,CPU 资源有限,任务需要轮流执行。 保存上下文确保任务切换后能从中断点继续执行,不会丢失执行状态, 这是实现任务并发和实时性的基础。
任务切换包括三个关键步骤:保存当前任务上下文到任务栈、选择下一个就绪任务、 恢复目标任务上下文并继续执行。整个过程由调度器统一管理。
本示例中主要触发来源为时间片调度(系统Tick)和 task yield 主动让出CPU。 不同触发点对应不同的调度策略,影响系统的实时性和响应性。
HRTOS 如何实现上下文切换
在 HRTOS v2 中,上下文切换的实现围绕任务寄存器环境保存、堆栈切换、调度器触发和恢复任务运行四个核心环节展开。 每个环节都经过精心设计,确保切换过程的高效性和可靠性。
保存内容:程序计数器、栈指针、通用寄存器、状态寄存器
保存位置:当前任务的任务栈
保存时机:调度触发时,在中断上下文中保存
设计考虑:最小化保存内容,减少切换开销
切换方式:修改栈指针寄存器(SP)指向目标任务栈
栈管理:每个任务拥有独立的栈空间
栈保护:防止栈溢出和栈冲突
恢复机制:从目标任务栈顶恢复执行现场
触发条件:时间片到期、任务主动让出(yield)、中断事件
调度算法:基于优先级的轮询调度
就绪队列:维护所有就绪任务的状态
选择策略:从就绪队列中选择最高优先级任务
恢复内容:从任务栈恢复寄存器和程序计数器
恢复时机:上下文切换的最后阶段
执行方式:通过中断返回指令(RETI)恢复执行
状态转换:任务从 Ready 状态进入 Running 状态
任务从创建到上下文切换的完整执行路径
上下文切换的完整实现
task_A 和 task_B 任务函数,分别控制不同的 LED
#include "hrtos.h"
#define TASK_A 1
#define TASK_B 2
sbit LED_A = P1^0;
sbit LED_B = P1^1;
/* Task A */
void task_A(void)
{
u16 i;
P1=0XFF;
while(1)
{
LED_A = ~LED_A; // 切换A灯状态
i=2000;
while(i--);
}
}
/* Task B */
void task_B(void)
{
u16 i;
while(1)
{
LED_B = ~LED_B; // 切换B灯状态
i=2000;
while(i--);
}
}
hrtos_main 函数,创建两个任务并启动调度系统
/* 系统入口页 */
void hrtos_main(void)
{
os_task_create((unsigned int)task_A, TASK_A, 3, 2);
os_task_create((unsigned int)task_B, TASK_B, 3, 2);
}
示例执行后的预期行为
使用本示例时需要关注的关键点
task_A 和 task_B 需要配置独立的任务栈空间,栈空间不足会导致上下文切换失败。 本示例使用默认栈配置,实际应用中应根据任务需求调整栈大小。
两个任务使用相同的优先级(优先级 3),在时间片调度下实现公平执行。 如果设置不同优先级,高优先级任务可能会抢占低优先级任务的执行。
上下文切换需要保存和恢复寄存器状态,存在一定的性能开销。 在实时性要求高的场景中,需要合理设置时间片长度,平衡切换频率和系统响应性。
本示例主要通过时间片调度触发切换,也可以通过 yield 主动让出 CPU。 在实际应用中,应根据任务特性选择合适的调度触发方式。
深入学习的相关资源