例如,操作系统不允许应用任务直接访问硬件,而是通过提供一些系统服务函数,用户任务使用SVC对系统请求服务。当用户想访问某个硬件资源时,便产生一个SVC异常,于是操作系统提供的SVC异常服务函数得到执行,它再调用相关的系统服务函数,完成用户的请求。这种请求——响应请求的方式有诸多好处:
1、 硬件控制由OS负责,使得用户程序无需了解繁杂的硬件控制细节,简化了开发难度,应用移植性更好;
2、 OS的代码一般会经过充分验证,从而使系统更加安全可靠;
3、 用户任务无须运行在特权模式,避免了不当操作导致整个系统出现问题的风险;
SVC指令需要一个8立即数来作为系统调用的代号,SVC异常服务例程会提取这个立即数,从而知道此次的调用请求。例如调用编号2的系统系统服务请求的汇编指令:
SVC #0x2
一些编译器会提供SVC调用的内建函数,在C/C++代码中调用该内建函数就会触发SVC异常。没有提供SVC内建函数的编译器一般是使用内联汇编的方式,在C/C++中插入SVC异常触发的汇编指令。
当SVC异常服务例程执行时又是如何获得本次请求的编号呢?这就需要了解Cortex-M的堆栈和栈帧结构了。因为SVC指令的第一个字节为SVC的编号,只需找到执行的这条SVC指令的本身就能获取编号的数值。Cortex-M有自动入栈机制,当进入到SVC异常之前,R0~R3、R12、LR、PC、xPSR这8个寄存器会被自动压栈。因此要获取PC,只需要从堆栈中将栈帧提取出来。但是Cortex-M有两个堆栈指针,分别是主堆栈指针MSP和进程堆栈指针PSP,于是再进一步就变成了寻找进入SVC异常前栈帧是压入了哪一个堆栈。