一个RTOS具有的基本功能
1. 任务调度策略
2. 内存管理
3. 中断处理
4. 共享资源的访问
1、理解几个基本问题:
Q1: RTOS最主要的特性(优势)是什么?
A1: 实时性,不同以往的前后台系统的轮询调度方法,RTOS通过CPU调度使得多个任务并发处理,每个任务都能得到更快的响应。
补充
.前后台系统:单任务系统,应用程序基本都放在一个大循环while(1)中,有时需要在中断中完成一些处理。中断服务函数即为前台程序,while(1)即为后台程序。
.轮询:由CPU定时发出询问,依序询问每一个任务是否需要其服务,有即给予服务,服务结束后再问下一个任务。
前后台系统中,各个任务都是排队等待执行的,只有轮到这个任务,它才能执行;实时性很差。
Q2:为什么RTOS能够并发处理多个任务?
A2:处理器在任一时刻只能执行一个任务。但通过快速的任务切换,一个多任务操作系统可以使它看起来好像每个任务并行执行一样。
关于并行与并发
的解释(来自知乎):
你吃饭吃到一半,电话来了,你一直到吃完了以后才去接,这就说明你不支持并发也不支持并行。
你吃饭吃到一半,电话来了,你停了下来接了电话,接完后继续吃饭,这说明你支持并发。
你吃饭吃到一半,电话来了,你一边打电话一边吃饭,这说明你支持并行。
并发的关键是你有处理多个任务的能力,不一定要同时。并行的关键是你有同时处理多个任务的能力。
Q3:编写自己的RTOS,首先要解决哪些核心问题
A3:
a. 系统心跳:SysTick初始化
b. SysTick和PendSV的优先级设置
c. 任务控制块TCB结构与堆栈初始化
d. 上下文切换:PendSV_Handler函数
e. 系统延时函数(阻塞,调度)
f. 任务调度:SysTick_Handler函数
2、了解Cortex-M3
寄存器组:
1、R0-R12 通用寄存器
R0-R12都是32位通用寄存器,用于数据操作。
但是注意,绝大多数16位thumb指令只能访问R0-R7,
而32位thumb-2指令可以访问所有寄存器。
2、Banked R13: 两个堆栈指针
Cortex-M3有两个堆栈指针,它们是banked,所以任一时刻只能使用其中之一。
MSP: 复位后缺省使用的堆栈指针,用于操作系统内核以及异常处理例程
PSP: 由用户的应用程序代码使用
(堆栈的最低两位永远是0,这意味着堆栈问题4字节对齐的)
在ARM编程领域,凡是打断程序顺序执行的事件,都被称为异常。除了外部中断,当有指令执行了“非法操作”,或访问被禁的内存区间,因各种错误产生的fault,以及不可屏蔽中断发生时,都会打断程序的执行,这些情况统称为异常。
3、怎么进行任务切换:
任务切换时,我们需要保存任务运行环境(xPSR,PC,LR,R0-R12这16个寄存器
)到任务堆栈,以便下次恢复运行。
Cortex-M3有两个堆栈指针,但同时刻只能使用一个,我们可以用一个来作为任务切换时保存“上下文”。
任务切换流程:从A任务切换到B任务,把任务A运行时的寄存器参数入栈,保存到任务A的栈中;再把任务B栈里的内容出栈处理,最后让程序计数器PC指向任务B( PC指向哪儿,处理器就去哪
)。
《Cortex-M3权威指南》给我们的提示:
在系统中使用双堆栈
CM3的出现,让单片机业界也能出双枪李向阳。v7-M架构的一个重要能力,就是提供了这个双堆栈的设计,允许把用户应用程序的堆栈与特权级/操作系统内核的堆栈分开
。(如果再辅以MPU,还能进一步地阻止用户程序访问内核的堆栈,同时也消除了内核数据被破坏的可能。)
要在CM3中创建可靠扛打的系统,必须两手抓,两手都要硬。典型地,一个真正健壮的CM3软件系统都要使用RTOS内核的,其通常会符合如下的要求:
a. 服务例程使用MSP
b. 尽管异常服务例程使用MSP,但是它们在形式上返回后,内容却可以依然继续—而且此时还能使用PSP,从而实现“可抢占式的系统调用”,大幅提高实时性能。
c.通过SysTick,实时内核的代码每隔固定时间都被调用一次
,运行在特权级水平上,负责任务的调度、任务时间管理以及其它系统例行维护。
d. 用户应用程序以线程的形式运行,使用PSP,并且在用户级下运行
e. 内核在执行关键部件的代码时,使用MSP(在辅以MPU时,MSP对应的堆栈只允许特权级访问)
假设系统内存是一块SRAM,则我们可以通过MPU,把它分为两个regions,其中一个用于用户级,另一个用于特权级。另外,CM3的堆栈是“向下生长的满栈”,因此需要把这两个SP初始为指向两个regions的顶端。
关键点:
①
SysTick中断,周期性地把执行点转入操作系统,从而使例行的系统管理以及必要轮转调度得以维持(SysTick作为系统“心跳”)
;
②CM3的堆栈是“向下生长的满栈”。
关于上下文切换
的提示:
PendSV(可悬起的系统调用),它和SVC协同使用。一方面,SVC异常是必须得到响应的(若因优先级不比当前正处理的高,或是其它原因使之无法得到响应,将上访成硬fault),应用程序执行SVC时都是希望所需的请求立即得到响应。另一方面,PendSV则不同,它是可以像普通的中断中断一样被悬起(不像SVC那样会上访)。
OS可以利用它“缓期执行”一个异常—直到其它重要的任务完成后才执行动作。悬起PendSV的方法是:手工往NVIC的PendSV悬起寄存器中写1。悬起后,如果优先级不够高,则将缓期等待执行。(PendSV优先级设为最低)
PendSV的典型使用场合是在上下文切换时(在不同任务之间切换)。例如一个系统中有两个或多个就绪的任务,上下文被触发的场合可以是:
a. 执行一个系统调用
b. 系统滴答定时器(SysTick)中断(转轮调度中需要)