ARM芯片中对于需要使用到操作系统的情况,SVC和PendSV是两个很重要的中断。但是在实际使用的过程中,不免会产生几个疑问。一个是这两个中断都是属于由用户来主动触发的中断,他们有什么区别?还有一个就是为什么会是使用SVC来提供系统服务访问入口,而PendSV则是做上下文切换?带着这些疑问,开始本篇的讲解
SVC(Supervisor Call请求管理调用)
调用SVC指令就会触发SVC中断,它的作用乍一看和普通调用函数有点像,不同的是它跳转的不是函数而是SVC中断handler,因此大家可以把它看成是一个交由用户控制的中断源。
它的编写方法如下图所示,资料来自与《Armv7-M Architecture Reference Manual》
SVC指令构成图
如下实例代码是编号为0的函数来触发Supervisor call
__asm void CallSupervisor( void ) { svc 0 }
那么问题来了,不同的编号触发的是同一个SVCall Handler中断处理函数,那么我们要怎么知道是哪个编号触发的呢?这个知识点就涉及到CortexM内核的异常压栈机制,具体可以参考公众号《如何知道程序是运行到哪里触发的中断》;
如下图范例可以看出,这里调用了一个编码为10的SVC指令,SVC指令地址为0x8001fa0,地址上的内容是0xdf0a。结合SVC指令构成图可知其二进制指令构成0xdf+编号,所以这里看到二进制的数据为0xdf0a,是个编号10的SVC指令。
根据异常压栈内容可知返回地址为0x8001af2,所以可得SVC指令编号 = 异常压栈后的返回地址-2。之所以减2是因为thumb指令为大小为2 bytes,刚好就是SVC指令的大小(0xdf0a)。可以看出执行完SVC指令立刻就会执行中断动作
PendSV(Pendable Service Call可挂起的系统调用)
PendSV其实和SVC有点像,所以很容易被混淆。其是通过使能中断控制状态寄存器中的PENDSVSET位,来实现触发。一般情况下PendSV的中断优先级都配置比较低,所以可以理解调用PENDSV后,这个中断触发允许被挂起,等没有优先级更高的中断需要运行的时候,才会触发PendSV中断,就是一个允许延时一会再执行的SVC。
/* Interrupt control state register:0xe000ed04 * Bit 28 PENDSVSET: PendSV Bit */ #define NVIC_INT_CTRL_REG( * ( ( volatile uint32_t * ) 0xe000ed04 ) ) #define NVIC_PENDSVSET_BIT( 1UL << 28UL ) #define CallPendSV() { portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; }
哼哈二将的职责
在操作系统中,通常使用SVC指令来请求系统调用,而PendSV来进行线程切换时上下文保存动作。刚开始学习操作系统的时候,有点困惑为什么要这样做功能划分,这两个中断都让我有点傻傻分不清楚。后来觉得其实这应该也不算是硬性规定,就是约定俗成这么干,理论上用哪个去申请系统调用和切换上下文都是可以的。这也不是随意猜测的,如下可以看几个例子,在FreeRTOS中第一个任务的调用就是用的SVC,后面就一直是使用PENDSV做上下文切换;而在UCOS和RT-Thread中则是从第一个任务开始就都是使用的PENDSV。
下图为FreeRTOS调度器首次运行时是使用SVC指令
下图示UCOS调度器首次运行时是使用PENDSV
下图示RT_Thread调度器首次运行时是使用PENDSV
那为什么CortexM要出这个SVC和PENDSV这两个由用户用来触发中断的指令,用来辅助操作系统呢?众所周知,CortexM内核有多重模式的,主要分为特权模式和非特权模式。默认情况下我们是处于特权模式,所以各位工程师可以愉快的修改各种内核寄存器,配置、开关中断。可以设想一下,如果用户代码可以肆意的开关中断,那对于操作系统就存在失控的隐患。因此出于运行安全上的考虑,操作系统是希望用户运行其代码的时候是处于非特权模式,仅有操作系统来接管内核寄存器的控制。而中断触发后会将非特权模式切换为特权模式,现在就清楚了SVC和PENDSV指令的作用就是提供让非特权模式下的用户层代码可以进入特权模式的接口,将权限交给操作系统进行内核以及寄存器的操作。完成操作后切换回非特权模式并返回用户层代码继续运行。
全部0条评论
快来发表一下你的评论吧 !