图 7.14 SVC作为操作系统函数门户示意图
SVC异常通过执行”SVC”指令来产生。该指令需要一个立即数,充当系统调用代号。SVC
异常服务例程稍后会提取出此代号,从而解释本次调用的具体要求,再调用相应的服务函数。
例如,
SVC 0x3 ; 调用 3 号系统服务
在 SVC 服务例程执行后,上次执行的 SVC 指令地址可以根据自动入栈的返回地址计算
出。找到了 SVC 指令后,就可以读取该 SVC 指令的机器码,从机器码中萃取出立即数,就
获知了请求执行的功能代号。如果用户程序使用的是 PSP,服务例程还需要先执行MRS Rn,
PSP 指令来获取应用程序的堆栈指针。通过分析 LR 的值,可以获知在 SVC 指令执行时,正
在使用哪个堆栈(细节在第 8 章中讨论)。
SVC vs. SWI
如果你曾使用过其它的 ARM 处理器(如 ARM7),你也许会知道那里有一个被称为
“软件中断”的指令(SWI)。SVC 的地位与 SWI 是相同的——而且连机器码都相同。
然而,因为在 CM3 中,异常处理模型已经“洗心革面”了,就故意把该指令也重命名,
以强调它是在新生的系统中使用的。并且让程序员在把 ARM7代码移植到 CM3 时,能
充分注意到这个本质的不同(至少必须得改名,每次改名时都得到警示)。
由 CM3 的中断优先级模型可知,你不能在 SVC 服务例程中嵌套使用 SVC 指令(事实上
这样做也没意义),因为同优先级的异常不能抢占自身。这种作法会产生一个用法 fault。同
理,在 NMI服务例程中也不得使用 SVC,否则将触发硬 fault。
另一个相关的异常是 PendSV(可悬起的系统调用),它和 SVC 协同使用。一方面,SVC
异常是必须立即得到响应的(若因优先级不比当前正处理的高,或是其它原因使之无法立即
响应,将上访成硬 fault——译者注),应用程序执行 SVC时都是希望所需的请求立即得到响
应。另一方面,PendSV 则不同,它是可以像普通的中断一样被悬起的(不像 SVC 那样会上
访)。OS可以利用它“缓期执行”一个异常——直到其它重要的任务完成后才执行动作。悬
起 PendSV 的方法是:手工往 NVIC的 PendSV悬起寄存器中写 1。悬起后,如果优先级不够
高,则将缓期等待执行。
PendSV的典型使用场合是在上下文切换时(在不同任务之间切换)。例如,一个系统中
122
资料整理自互联网,版权归原作者! 欢迎访问 www.XinShiLi.net 新势力单片机、嵌入式
1