系列一:微型操作系统内核源码详解系列一: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博客
经历了前面的文章的铺垫,一切都准备就绪,接下来我们将会在SVC中断服务函数中开启第一个任务。
让我们看看SVC中断响应:
首先看前三行代码:
先令r3寄存器的值为pxCurrentTCB的地址,再把这个地址指向的内容给r1,现在r1存储的就是指针,我们知道指针就是地址,再把这个指针的值给r0,那么r0就会找到任务控制块。
pxCurrentTCB的作用是指向当前运行的任务或即将运行的任务的控制块(TCB),TCB是一个存储任务各种信息的数据结构。这三行代码的作用是获取pxCurrentTCB指向的任务栈,因为TCB的第一个成员就是栈顶指针。
现在重点来了,请看下面这行代码:
让我们先回顾一下我们在初始化stack时是如何操作的:
请读者不要误解,图中这些R代表的是这些内存地址的内容要被加载到哪个寄存器,不是寄存器在这些地方。
cm3的栈是向下增长,所以栈顶在下面。
这行代码的意思就是:从r0这个内存地址开始,把栈中往上九个地址的内容依次加载到CPU的寄存器r4-r11和r14,然后r0自增(加载后r0寄存器刚好表示这个栈里面的r0位置),也正好把portINITIAL_EXEC_RETURN加载到r14,不知道读者对这个定义还有没有印象,现在该回收伏笔了:
读者发现没有,下一行代码使用的是psp,而不是msp。看了上面的信息,我们可以推断,在把portINITIAL_EXEC_RETURN加载到r14后,如果我们进行跳转,就能成功实现从异常服务模式到线程模式的转换,那么程序就会转为使用线程堆栈,也就是说,在SVC调用结束后,我们将使用psp作为堆栈指针。
再看下一句:
现在的r0刚好对应这个栈里面的存储形参的位置(不得不说,笔者还以前怀疑过prvparameter的作用,现在发现它真的设置得非常好,虽然它并不是什么有特殊含义的参数,但是它贯穿了整个rtos架构,起到了连接和中继作用),执行任务时将会使用psp,于是我们更新psp这个栈指针。
isb是等待指令完成防止指令乱序导致缓冲区出现错误。
(这里笔者还是简单解释一下,处理器在执行指令时会使用流水线和并行这些技术,也就是说,指令并不是依次执行的,可能是同时执行的,这种方法提高了效率,但是可能导致错误的发生,于是指令集被开发时,还开发了这些特殊的指令针对那些对内存操作顺序有严格要求的操作)
这里就是清零r0寄存器,然后设置basepri寄存器为0,就是打开所有中断,basepri寄存器是一个用来控制屏蔽中断的寄存器,我们将会在临界区学习它,读者可以看看这些:
接下来看最后一行代码:
这里的意思就是异常返回时,跳转到r14代表的地址那里,前文已经提过了,在SVC调用结束后,我们将使用psp作为堆栈指针,r14存储的值的含义就是完成异常服务模式到线程模式的转换,这里就是完成转换的具体跳转指令。
这样,第一个任务就被执行了。
以上就是SVC调用的内容了,接下来笔者将会讲解PendSV中断服务,PendSV响应可以说是FreeRTOS调度器的精髓所在。