【声明】本博客所有内容均为个人业余时间创作,所述技术案例均来自公开开源项目(如Github,Apache基金会),不涉及任何企业机密或未公开技术,如有侵权请联系删除
背景
上篇 blog
【OS】【Nuttx】【栈溢出】up_initial_state(IPSR&EPSR)
分析了 IPSR 中存放着正在处理的异常编号,以及 IPSR 为 0 时,表示 Thread Mode,非异常状态,也就是普通任务模式,然后分析了 EPSR,EPSR 中除了有 Thumb 位之外,还有个 IT(If-Then)状态位,用来支持 Thumb-2 的条件执行语句,并详细分析了 APSR 中的 Z 标志,Z = 1 表示的是上一次比较时,或运算结果为 0,也就是相等,下面继续分析
栈溢出检测
OK,之前 blog 分析了 up_initial_state 中关于 xPSR 寄存器的设置,主要设置的,就是 Thumb 位,下面继续看剩余 CPU 寄存器的设置
- (可选)设置
rBS(R10)用于栈检查
c
#ifdef CONFIG_ARMV7M_STACKCHECK
xcp->regs[REG_R10] = tcb->stack_alloc_ptr + 64;
#endif

这里 + 64 字节之前解释过,就是为了留出安全距离
OK,之前 blog 【OS】【Nuttx】【最小系统】任务跳转 介绍了在 SIM 仿真环境下的任务跳转,下面再看下经过 up_initial_state 任务初始化完成后,Cortex-M 架构的任务跳转以及上下文切换
先回顾下之前 SIM 环境下的任务跳转,首先是 up_switch_context

首先是函数名
up_是 Nuttx 中表示与体系结构相关的前缀,与底层(芯片架构)区分开,表示平台层switch_context:表示平台功能,即切换上下文
所以 up_switch_context 作用是在不同任务(线程)之间自由切换 CPU 上下文
然后是 up_switch_context 函数的注释,从描述上看,当有一个任务位于就绪队列(ready-to-run list)中,并且已经被调度器选中准备运行,那么就可以通过 up_switch_context 恢复准备要执行任务的上下文(比如寄存器状态,程序计数器等),并开始执行
接下来是参数说明,up_switch_context 涉及到两个参数
struct tcb_s* tcb:指向下一个要运行的任务,也就是即将被调度上 CPU 的任务,该任务是就绪队列的头部(head of ready-to-run list)struct tcb_s* rtcb:指向当前正在运行的任务(running task),这个任务即将被暂停(比如因为时间片用完,主动让出 CPU,或被更高优先级的任务抢占)
注意,tcb_s 是 Task Control Block(任务控制块)的结构体类型,其中保存了任务的所有信息,包括栈指针,寄存器状态,优先级,任务状态(运行/就绪/阻塞等)
up_switch_context 函数会完成任务切换的核心操作,具体包括
- 保存当前运行任务
rtcb的上下文 :把 CPU 寄存器(如 PC,SP,通用寄存器等)保存到rtcb的栈或 TCB 中 - 恢复下一个任务
tcb的上下文 :从tcb的栈或 TCB 中加载之前保存的寄存器值 - 跳转到新任务的执行位置继续运行:通过修改栈指针 SP 和程序计数器 PC 实现,最终跳转到新任务的代码处
举个简单例子方便理解
- 假设系统中有两个任务 A 和 B,当前 CPU 正在运行任务 A(
rtcb= A)

- 调度器决定切换到任务 B(
tcb= B)

- 调用
up_switch_context(B, A),保存 A 的现场(寄存器等),恢复 B 的现场 - CPU 开始执行 B,好像 B 从未中断过

OK,本篇先到这里,如有疑问,欢迎评论区留言讨论,祝各位功力大涨,技术更上一层楼!!!更多内容见下篇 blog
【OS】【Nuttx】【ARMV7M】任务跳转(上下文切换)(二)