微型操作系统内核源码详解系列五(六):Pendsv切换任务下篇

系列一:微型操作系统内核源码详解系列一:rtos内核源码概论篇(以freertos为例)-CSDN博客

系列二:微型操作系统内核源码详解系列二:数据结构和对象篇(以freertos为例)-CSDN博客

系列三:微型操作系统内核源码详解系列三(0):空间存储及内存管理篇(前置篇)-CSDN博客

                微型操作系统内核源码详解系列三(1):任务及切换篇(任务函数定义)-CSDN博客

                微型操作系统内核源码详解系列三(2):任务及切换篇(任务函数定义)-CSDN博客

                微型操作系统内核源码详解系列三(3):任务及切换篇(任务函数定义)-CSDN博客

                微型操作系统内核源码详解系列三(4):arm架构篇-CSDN博客

                微型操作系统内核源码详解系列三(5):进程与线程-CSDN博客

系列四:

 ​​​​​微型操作系统内核源码详解系列四(1):操作系统调度算法(linux0.11版本内核)-CSDN博客

微型操作系统内核源码详解系列四(2):操作系统调度算法(rt-thread内核)-CSDN博客

微型操作系统内核源码详解系列四(3):操作系统调度算法(FreeRTOS内核篇上)-CSDN博客

微型操作系统内核源码详解系列四(4):操作系统调度算法(FreeRTOS内核篇下)-CSDN博客

系列五:

微型操作系统内核源码详解系列五(1):arm cortex m3架构-CSDN博客

微型操作系统内核源码详解系列五(2):cm3下栈的初始化-CSDN博客

微型操作系统内核源码详解系列五(3):cm3下调度的开启-CSDN博客

微型操作系统内核源码详解系列五(四):cm3下svc启动任务-CSDN博客

微型操作系统内核源码详解系列五(五):cm3下Pendsv切换任务上篇-CSDN博客

微型操作系统内核源码详解系列五(六):Pendsv切换任务下篇-CSDN博客

经过层层铺垫,我们将迎来主线的尾声,一个os的真正面纱将会被我们揭开。

不知道笔者的文章是否对你有益,笔者后面预计会把全部内容转移到个人网站上,等笔者完善一下网站前后端,会公布网址,欢迎大家前来学习。

__asm void xPortPendSVHandler( void )函数在FreeRTOS中,是真正实现任务切换的函数,上下文切换会发生在这里,简单看一看:

通过SVC调用,其实我们已经学会了怎么把任务加载到CPU寄存器,这就是下文,与上文指令相反的操作,就是上文。

请参考stack的分布:

当PendSV中断响应时,spsr,pc,(r14)lr,r12,r3,r2,r1,r0的值会被自动存储到任务栈中,有读者可能会好奇,这是怎么找到任务栈的呢?不要忘记了PSP!它会指向这个任务栈的栈顶。

前面几行是声明,PRESERVE8表示八字节对齐。

让我们来看看这些代码:

通过前文分析可知,cpu以psp作为任务栈的参考,自动加载了一些寄存器的值到任务栈,那么psp现在指向哪里呢?

它现在指向之前r0的值,注意,r0的值不是CPU的寄存器r0:

所以,之前的指令,就是为了获取栈中r0的值的位置。isb前面的博客已经解释过了,让我们跳转到下面的代码。

现在我们要手动保存stack中r0数据下面的值。

首先,我们要获取上文任务的pxCurrentTCB:

通过前几篇博客我们知道,pxCurrentTCB指向的是TCB,也就是任务控制块,前一行代码,是将pxCurrentTCB这个指针的地址加载到r3;后一句,是将这个地址指向的内容加载到r2,也就是说,r2现在的内容就是pxCurrentTCB这个指针;如果再进行一次ldr操作,就能找到TCB了,读者可以去SVC调用的篇章看看。

这里是判断是否使用了浮点计数器,以及是否保存相关寄存器。

tst r14, #0x10: 这条指令执行按位与操作,将寄存器 r14 和 0x10 进行按位与操作,按位与操作的结果会影响条件编码寄存器的状态。

it eq是判断语句,根据条件编码寄存器的状态决定是否执行下一行代码。

vstmdbeq r0!, {s16-s31}:以r0作为基地址,存储相关寄存器{s16-s31}的值到栈。

以r0作为基地址,手动保存r4到r11,r14的值。

现在,r2该派上用场了,r2的值是pxCurrentTCB这个指针。这句汇编的意思就是,把r0的值存储到pxCurrentTCB指向的内容,pxCurrentTCB指向的是任务控制块,而任务控制块的第一个地址是栈顶指针,所以说r0的值被存储到了栈顶指针。

至此,上文任务的保存已经结束了。

让我们来到下文,我们需要把下文任务栈加载到CPU寄存器:

cs和sp表示一个栈的双指针,这里是将r3压入栈,r3保存的是pxCurrentTCB指针的地址。这是为了防止r3在调用vTaskSwitchContext函数时被更改。

接下来的这几行代码笔者之前讲过,就是进入临界区,关闭异常和中断,防止被打断:

现在要执行vTaskSwitchContext函数了,这就是调度选择算法,选择下一个要执行的任务。如果读者忘记了,可以回到调度算法的文章再看一看,

然后就是退出临界区了。

加载对应的任务栈的内容到寄存器,上下文切换是对称的。切换下文的内容可以参考SVC调用及之后的博客,笔者在这几篇博客里已经讲得非常详细了,读者看几遍就能理解了:

来到后一段代码,

msr psp,r0是为了更新psp的值,当异常退出时,会将PSP作为基本地址将任务栈的相关内容自动加载到寄存器。

这个是针对特定硬件XMC4000的矫正,XMC4000是一个单片机家族,类似于stm32,它采用的是cm4架构,也就是说,如果你的CPU是XMC4000,定义了相关的宏,考虑mcu的差异,会再进行以下操作:

push { r14 }: 将寄存器 r14 的值压入堆栈
pop { pc }: 将堆栈中的值弹出到程序计数器 pc 中,从而实现一个间接的无条件跳转
nop是一个延时操作,表示什么也不做,是为了保证指令流执行的正确性。

最后一句代码:

跳转到了r14处,也就是返回任务模式。

至此,微型操作系统内核系列主线就完结了,后面笔者会考虑出一些支线内容,可能也会讲一讲信号和互斥这些概念,希望大家都能学有所得吧!

  • 7
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值