4.2.2 线程的状态及其切换
Hello China的线程可以处于以下几种状态。
(1)Ready:所有线程运行的条件就绪,线程进入Ready队列,如果Ready队列中没有比该线程优先级更高的线程,那么下一次调度程序运行时(时钟中断或系统调用),该线程将会被选择投入运行。
(2)Suspended:线程被挂起,这是线程执行SuspendKernelThread的结果,或者该线程创建时就指定初始状态为Suspended,处于这种状态的线程,只有另外的线程调用ResumeThread时才能把该线程的状态改变为Ready。
(3)Running:线程获取了CPU资源正在运行。在单CPU环境下,任何时刻只有一个线程的状态是Running,但在多CPU环境下,假设有N个CPU,那么任何时刻,最多的时候,可能有N个线程的状态是Running。
(4)Sleeping:线程处于睡眠状态,一般情况下,处于运行状态(Running)的线程调用Sleep函数,则该线程进入睡眠队列,当定时器(由Sleep函数指定)到时后,处于该状态的线程被系统从Sleeping队列中删除,并插入Ready队列,相应地,其状态修改为Ready。
(5)Blocked:线程不具备运行的条件,比如,线程正在等待某个共享资源,那么该线程就会进入该共享资源的队列中,其状态也会被修改为Blocked。当共享资源被其他资源释放的时候,会重新修改当前等待该共享资源的线程(Blocked线程),把其状态修改为Ready,并放入Ready队列。
(6)Terminal:线程运行结束,但用于对该线程进行控制的线程对象(Kernel Thread Object)还没有被删除,处于这种状态的线程对象被放入Terminal队列,直到另外的线程明确地调用DestroyKernelThread删除该线程对象为止。
其中,每种状态的线程对象被组织在一个队列中(在单CPU环境中,状态是Running的线程不进入任何队列,在多CPU环境中,状态是Running的线程,进入lpRunningQueue队列),每个队列都是一个优先队列对象,因此,位于其中的线程对象可以按照优先级进行排序,对于状态是Blocked的线程,被组织在共享资源(或同步对象)的本地等待队列中,调度程序只选择Ready队列中的线程投入运行,整个系统的线程对象队列模型参考图4-1。
图4-1 Hello China的线程队列
从图4-1中可以看出,系统总共维护4个队列(lpSleepingQueue,lpSuspendedQueue,lpReadyQueue和lpTerminalQueue),调度程序(Scheduler)只从lpReadyQueue中选择线程投入运行。对于共享对象(SharedObject),在Hello China的实现中,被组织成了一条链表(ObjectManager维护),每个共享对象维持一个等待队列(Waiting Queue),凡是进入这些队列的线程对象,其状态必然是Blocked。
图4-2给出了线程各个状态之间的转换图示。其中,箭头表示转换方向,单向的箭头代表状态转换是单向的,比如Running到Suspended的单向箭头,代表一个处于Running状态的线程,可以切换到Suspended状态,反之则不行。
图4-2 核心线程的状态及其之间的转换
从图4-2可以看出,Running状态只能从Ready状态转换过来,Blocked状态和Suspended状态也只能从Running状态转换过来。
表4-3列出了各状态线程之间的切换条件。
表4-3 线程转换发生的条件
表4-3中,横的一栏为初始状态,对应的表格为转换原因,竖的一栏为目标状态。