调度程序如何知道任务处于阻塞状态?

How scheduler knows a Task is in blocking state?

我正在阅读 David E.Simon 的 "Embedded Software Primer"。 其中讨论了 RTOS 及其构建块 Scheduler 和 Task。它表示每个任务都处于就绪状态、运行 状态或阻塞状态。我的问题是调度程序如何确定任务处于阻塞状态?假设它正在等待信号量。那么很可能 Semaphore 处于无法 return 的状态。 Scheduler 是否会查看某个函数是否 return,然后将其状态标记为 Blocking?

实现细节因 RTOS 而异。通常,每个任务都有一个状态变量,用于标识任务是就绪、运行ning 还是阻塞。调度程序只是简单地读取任务的状态变量来确定任务是否被阻塞。

每个任务都有一组参数,用于确定任务的状态和上下文。这些参数通常存储在结构中并称为 "task control block"(尽管实现因 RTOS 而异)。 ready/run/block 状态变量可能是任务控制块的一部分。

当任务试图获取信号量但信号量不可用时,任务将被设置为阻塞状态。更具体地说,semaphore-get 函数会将任务从 运行 更改为阻塞状态。然后将调用调度程序以确定接下来应该 运行 执行哪个任务。调度程序将读取任务状态变量,不会 运行 那些被阻止的任务。

当另一个任务最终设置信号量时,阻塞在信号量上的任务将从阻塞状态变为就绪状态,并且可以调用调度程序来确定是否应该发生上下文切换。

因为我正在编写一个 RTOS ( http://distortos.org/ ),我想我可以插话。

保存每个线程状态的变量确实通常在RTOSes中实现,这包括我的版本: https://github.com/DISTORTEC/distortos/blob/master/include/distortos/ThreadState.hpp#L26 https://github.com/DISTORTEC/distortos/blob/master/include/distortos/internal/scheduler/ThreadControlBlock.hpp#L329

然而,这个变量通常只用作调试辅助或额外检查(比如防止你启动一个已经启动的线程)。

在针对深度嵌入式系统的 RTOS 中,ready/blocked 之间的区别通常是使用容纳线程的容器来实现的。通常线程在链表中是"chained",通常也是按优先级和插入时间排序的。调度程序有自己的线程列表 "ready" ( https://github.com/DISTORTEC/distortos/blob/master/include/distortos/internal/scheduler/Scheduler.hpp#L340 ). Each synchronization object (like a semaphore) also has its own list of threads which are "blocked" waiting for this object ( https://github.com/DISTORTEC/distortos/blob/master/include/distortos/Semaphore.hpp#L244 ) . When a thread attempts to use a semaphore that is currently not available, it is simply moved from the scheduler's "ready" list to semaphores's "blocked" list ( https://github.com/DISTORTEC/distortos/blob/master/source/synchronization/Semaphore.cpp#L82 ). The scheduler doesn't need to decide anything, as now - from scheduler's perspective - this thread is just gone. When this semaphore is now released by another thread, first thread which was waiting on this semaphore's "blocked" list is moved back to scheduler's "ready" list ( https://github.com/DISTORTEC/distortos/blob/master/source/synchronization/Semaphore.cpp#L39 ).

通常不需要特别区分准备就绪的线程和实际运行ning 的线程。由于实际可以 运行 的线程数量是固定的,并且等于可用的 CPU 核心数,那么您所需要的只是每个 CPU 核心的指针,它指向来自"ready" 列表在那一刻处于那个核心 运行ning。在我的系统中,我做同样的事情 - "ready" 列表头部的线程是 运行ning 的线程,但我还管理一个指向该线程的迭代器(https://github.com/DISTORTEC/distortos/blob/master/include/distortos/internal/scheduler/Scheduler.hpp#L337)。您可以为 运行ning 个线程创建一个单独的列表,但在大多数情况下,这会浪费 space(通常只有一个),并且会使其他事情稍微复杂一些。

如果您有兴趣,我实际上已经写了一篇关于线程状态及其转换的文章 - http://distortos.org/documentation/task-states/这篇文章没有特别区分 "ready" 线程和 "ready" 线程实际上 运行ning。我认为这种区别实际上对任何事情都没有用,只要您有其他方法可以判断 "ready" 线程中的哪个是 运行ning.