FreeRTOS-为什么关中断之后切换进程?

 
一. 基本问题
 
FreeRTOS会在关键区即taskENTER_CRITICAL()和taskEXIT_CRITICAL()包裹的区间中,执行进程切换。即在关闭中断的时候,进行进程切换。
我们已经知道,即便关闭中断,PowerPC的sc中断,还是可以得到响应。但是时钟中断呢?这是个外部中断,无法得到响应。
那么是何时打开的中断呢?新进程切入之后,立即打开中断?在多个TASK级别做开关中断配对?
 
1.关键区和关键资源
 
或者叫竞争区间,使用PV原语保护,操作系统基本概念。在FreeRTOS中,为使用taskENTER_CRITICAL()和taskEXIT_CRITICAL()包裹的区间。
关键区中使用即PV原语保护的资源为关键资源。
 
2.基本同步机制:开关中断
 
单核CPU存在两个并行流程:TASK和中断ISR。中断可以打断TASK的执行。所以TASK中的关键区间需要使用开关中断保护,而中断ISR就不需要再关中断了。这是最基本的同步机制。
如果中断存在优先级,高优先级中断可以打断低优先级中断,则存在三个并行流程:TASK、低优先级中断ISR和高优先级中断ISR。如此仅仅关闭中断,已经无法起到保护关键区的作用。需要把CPU的当前中断响应级别提升到关键区间所需要的最高级别。这里的所需要,是指会使用到被保护资源的最高优先级的中断的优先级。即便不是最高,但其上优先级的中断ISR不使用被保护资源,则不会导致死锁。
多核CPU除了考虑本核的同步外,还需要考虑与其他核的同步,需要使用系统级别的标志。因为多个核共享同一个内存空间,所以这个同步标志,可以是一个存在内存中的变量。
 
3.关键区尽量短
 
关闭中断,会使CPU无法及时响应外部中断。所以为了提高响应的实时性,需要使关键区间尽量短。也就是关闭中断的时间尽量短。不涉及关键资源的操作,尽量放在关键区间之外执行。
 
4.PV操作成对出现,降低复杂度
 
为了降低程序的复杂度。使同步操作更易阅读理解,同时降低出错的概率。PV操作一般成对出现,并且出现在同一个函数中。为了保证成对出现,甚至可以使用goto语句。即使用if判断到需要直接退出的例外情况时,不是直接在if中开中断然后退出,而是使用goto跳转到成对的唯一一个开中断操作。
 
5.关键区间中切换进程提升了复杂度
 
所以关键区间中,在关闭中断的状态下切换进程,提升了代码的复杂度。如何保证开关中断的动作成对出现,变成了一个很复杂的问题。
下面我们跟踪xSemaphoreGive/xSemaphoreTake的流程,来看何时打开的中断。
 
二.Semaphore
 
Semaphore的使用队列实现。xSemaphore的Give和Task分别向队列中存和从队列中取。
 
1.xSemaphoreGive
 
进入关键区之后,如果队列不为空,则立即切换进程。从注释可以看出,这里有两种实现:a.立即切换进程;b.这里只记录状态,在退出关键区时执行切换。这个跟port的实现有关,跟FreeRTOS无关。
 
另外,这里也不一定会切换进程,跟进程的优先级有关。如果等待信号量的进程优先级比当前进程优先级低,则不会进行切换。这里我们假设会切换。
 
2.xSemaphoreTake
如果队列为空则在箭头处睡眠等待。同时,队列被存入元素后,也是在这里被唤醒。唤醒后,执行如下代码检查队列是否为空:
可以看到,这里又重新执行了一次taskENTER_CRITICAL(),取得队列元素之后,退出时又执行了一次taskEXIT_CRITICAL(),这是成对的操作。所以不存在被切入的进程打开进程切换前关闭的中断的情况。
问题没有解决:中断时何时被打开的?
 
三.中断嵌套实现
 
FreeRTOS自带的例子中,有两种实现。
 
1.自定义嵌套层级变量
 
2.使用TCB中的嵌套层级变量
 
即直接使用FreeRTOS的vTaskEnterCritical/vTaskExitCritical,这两个函数:
 
两个的区别在于一个使用全局变量,一个使用每TASK变量存储中断嵌套层级。
为什么每一个TASK一个中断嵌套层级呢?
 
四.谁的中断嵌套层级
按常规的理解,中断属于CPU,而不属于TASK,一个CPU上可以有多个TASK在执行。而CPU是全局的,所以中断状态也应该是全局的,所以中断嵌套层级也应该使用全局变量存储。
 
每个TASK一个中断嵌套层级,如何理解?
 
我们知道每个TASK记录自己的各个寄存器的值,是可以理解的。以便切入时恢复之前的状态。难道每个TASK也维护执行时中断状态,以便切入时恢复执行?难道每个TASK也记录中断的开关状态,切入时恢复之前的中断开关状态?
 
看一下TASK切换时保存和恢复的寄存器,以PowerPC为例。
 
五.中断发生时保存和恢复的寄存器状态
 
 
SRR1中保存着中断发生前的MSR寄存器的值,也包括EE位。那么恢复时,自然也会恢复EE位。
 
六.总结
 
首先回答何时打开中断的问题:进程切入时,即恢复进程切出前的中断开关状态。
另外一个结论:中断状态在物理上虽然是全局的,但在软件上是每个TASK维护一份。
PS. 复杂度还是提升了,OS层需要加倍小心,好在APP层无需过多考虑

 

猜你喜欢

转载自www.cnblogs.com/wjcdx/p/9388330.html