我正在阅读"嵌入式软件入门"作者:David E.Simon。 在其中讨论了RTOS及其构建块Scheduler和Task。它表示每个任务处于就绪状态,运行状态或阻塞状态。我的问题是调度程序如何确定任务处于阻塞状态?假设它正在等待信号量。然后它可能信号量处于无法返回的状态。 Scheduler是否查看某个函数是否未返回,然后将其状态标记为Blocking?
答案 0 :(得分:1)
实施细节因RTOS而异。通常,每个任务都有一个状态变量,用于标识任务是否已就绪,正在运行或已阻止。调度程序只是读取任务的状态变量以确定任务是否被阻止。
每个任务都有一组参数,用于确定任务的状态和上下文。这些参数通常存储在结构中并称为“任务控制块”(尽管实现因RTOS而异)。就绪/运行/阻止状态变量可以是任务控制块的一部分。
当任务尝试获取信号量且信号量不可用时,任务将被设置为阻塞状态。更具体地说,信号量获取功能会将任务从运行更改为阻止。然后调用调度程序以确定下一个应该运行的任务。调度程序将读取任务状态变量,并且不会运行被阻止的任务。
当另一个任务最终设置信号量时,信号量上阻塞的任务将从阻塞状态更改为就绪状态,并且可以调用调度程序以确定是否应该发生上下文切换。
答案 1 :(得分:1)
当我正在编写RTOS(http://distortos.org/)时,我想我可能会插话。
保存每个线程状态的变量确实通常在RTOS中实现,这包括我的版本: 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中,就绪/阻止之间的区别通常是使用容纳线程的容器来实现的。通常线程被链接"在链表中,通常还按优先级和插入时间排序。调度程序有自己的线程列表,这些线程已准备好" (https://github.com/DISTORTEC/distortos/blob/master/include/distortos/internal/scheduler/Scheduler.hpp#L340)。每个同步对象(如信号量)也有自己的线程列表,这些线程被"阻塞"等待这个对象(https://github.com/DISTORTEC/distortos/blob/master/include/distortos/Semaphore.hpp#L244)。当一个线程试图使用当前不可用的信号量时,它只是从调度程序"准备好"列表信号量"阻止"列表(https://github.com/DISTORTEC/distortos/blob/master/source/synchronization/Semaphore.cpp#L82)。调度程序现在不需要决定任何事情 - 从调度程序的角度来看 - 这个线程刚刚消失。当这个信号量现在由另一个线程释放时,第一个线程正在等待这个信号量" s"阻塞"列表被移回到调度程序"准备好"列表(https://github.com/DISTORTEC/distortos/blob/master/source/synchronization/Semaphore.cpp#L39)。
通常,不需要对准备好的线程和实际运行的线程进行特殊区分。由于可以实际运行的线程数量是固定的并且等于可用CPU内核的数量,因此您需要的只是每个CPU核心的指针,该指针指向来自" ready"那个时刻在那个核心运行的列表。在我的系统中,我也是这样做的 - "准备好" list是正在运行的那个,但我也管理一个指向该线程的迭代器(https://github.com/DISTORTEC/distortos/blob/master/include/distortos/internal/scheduler/Scheduler.hpp#L337)。你可以有一个单独的列表来运行线程,但在大多数情况下,这将浪费空间(通常只有一个),并使其他事情稍微复杂。
我实际上写了一篇关于线程状态及其转换的文章,如果你感兴趣的话 - http://distortos.org/documentation/task-states/这篇文章没有特别的区别是" ready"以及实际运行的那个。我不认为这种区别实际上对任何事情都有用,只要你有其他方法可以告诉哪些"准备好"线程正在运行。