FreeRTOS上下文切换

3
我正在使用FreeRTOS进行我的项目,并阅读文档,有些内容我无法理解。我知道当时钟中断被触发时会发生上下文切换,因此调度程序执行其工作并解除等待事件的任务并选择处于就绪状态的更高优先级任务。但是当任务在时钟中断之前阻塞时会发生什么?文档似乎暗示立即发生上下文切换(例如,两个具有不同优先级的任务分别调用vTaskDelay()以释放CPU时间片)。这是如何发生的?我搜索了但找不到任何答案。
编辑:在我的FreeRTOS端口(SAMD21 Cortex-M0+)中,使用portYIELD()宏,它只是请求SVCall异常,那么这是用于执行上下文切换的机制吗(除了调度程序运行时钟中断之外)?

1
不确定您的确切问题是什么,但如果一个线程在其时间片结束之前放弃处理器,则实时操作系统应立即运行调度程序。 - Fiddling Bits
@FiddlingBits 是的,我也是这么认为的,但文档中没有任何迹象。它只谈到了 ISR 在每个周期性 tick 上运行,但是在 ticks 之间发生的上下文切换呢?调度程序会接管并执行上下文切换吗?如果是这样,那么是否需要触发中断(软件)来完成它? - Luca
最新的M0端口不再使用SVC。然而,我预计SVC中断会触发PendSV中断来执行实际的上下文切换。 - Realtime Rik
4个回答

8
有关taskYIELD的文档。此函数可用于请求上下文切换,因此无需等待滴答声。上下文切换是特权操作,因此通常由软件中断执行。在您的情况下,由PendSV和SVCall执行。
如果所有任务都被阻塞(例如通过vTaskDelay),则FreeRTOS正在运行空闲任务vTaskDelay在内部使用portYIELD请求上下文切换,因为没有办法继续当前任务。
您还需要了解抢占式多任务处理才能理解FreeRTOS在该模式下的操作。 编辑2016-01-29:
  • 调用延迟函数会导致内部调用taskYIELD/portYIELD。因此,当前任务被阻止,并且FreeRTOS重新安排到可以运行(未被阻止)的最高优先级任务或空闲任务(如果没有任务可以运行)。
  • 调用例如xQueueReceive可能有两种可能性。队列中有一些元素,因此它被POPed。队列中没有元素,因此该任务切换到阻止状态并为您调用YIELD,因此FreeRTOS重新安排到另一个任务。
  • 调用例如xQueueSend可能有两种可能性。队列中没有空间,因此任务被阻止,直到有一些空间。至少有一个空闲元素,因此您可以将其推送到队列中。
从队列接收元素或将元素发送到队列可能会唤醒执行相反操作但当前已被阻止的其他高优先级任务。FreeRTOS将立即重新安排它。
这也可以从中断处理程序中完成。您可以有一个正在等待某个队列的处理程序任务。从中断中,将一些元素放入队列中。中断结束后,FreeRTOS重新安排到等待该队列的任务。只是要求该任务具有足够的高优先级。这具有好处,即在中断中不做太多工作-只是进行一些清理并将项目发送到队列中-这是短操作。 可以通过xTimerPendFunctionCallFromISR来处理中断,这是另一种有趣的方法。
请也阅读有关FreeRTOS基础知识的内容。这里有多个章节可供参考,内容很有趣。

如果一个任务被阻塞了,你不需要调用taskYIELD()。如果任务被阻塞了,你怎么可能调用它呢? - Richard
如果一个任务被阻塞了,就没有办法从这个任务中调用任何东西,因为它被阻塞了。RTOS 不会运行被阻塞的任务。 - j123b567

2
每当发生诸如TaskDelay或将项目发送到队列或释放资源等操作时,操作系统会判断是否有其他任务准备好运行,如果高优先级任务已经准备好运行,则它将抢占当前任务。基本上,准备好运行的最高优先级任务将运行。只有当两个或多个同等且最高优先级的任务准备好运行时,才会进行时间片轮转。
是的,中断会触发。在Cortex-M上,PENDSV中断执行上下文切换。

好的,如果我调用vTaskDelay()函数,当前任务将调用taskYield()函数并触发(软件)中断,让调度程序完成其工作。如果我将一个项目放入队列中(或者给出二进制信号量或另一个同步事件),内核会检查是否有其他更高优先级的任务正在等待该队列?在所有这些情况下,是被调用的函数每次执行检查并最终触发中断吗? - Luca
我认为这基本上是正确的。调用的函数会调度并执行其任务。外部中断也可以发出操作系统调用,从而导致另一个任务变为就绪状态。 - Realtime Rik

1
你是在询问当当前运行的任务阻塞时,如何切换到另一个任务?还是在询问上下文切换的机制?这并不清楚。
如果你在询问如何在一个任务阻塞时切换到另一个任务,那么答案就是什么都不需要做,这是RTOS为你提供的基本功能。例如,如果你调用vTaskDelay()或指定了阻塞时间的xQueueReceive(),则该任务进入阻塞状态。当任务被阻塞时,它无法运行,因此RTOS中的调度算法会选择下一个要运行的任务并启动该任务的运行。

我想了解其中的机制。它是如何发生的? - Luca
@Luca,请阅读FreeRTOS基础知识,因为我在我的回答中提到了它。 - j123b567

1
有点晚了。 我相信在xQueueReceive内部调用了一个隐式的taskYield。
在ISR中,调用xQueueSend不会调用taskYield。必须显式地调用它(并在函数末尾)。这使得ISR具有一定的控制权,可以决定是否进行上下文切换。 因此,如果一个任务被阻塞在xQueueReceive上,例如在Idle任务中,并且调用了执行xQueueSendFromISR的ISR。如果没有调用taskYield,它将返回到Idle任务,直到下一个计时器滴答声。即使xQueueReceive可以解除阻塞。 如果在ISR中调用taskYield,则调度程序将被调用,xQueueReceive将解除阻塞,并可能在ISR之后立即运行。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接