4.2.3 核心线程对象
核心线程对象(KernelThreadObject)用于管理Hello China操作系统中的所有线程,该对象记录了每个线程的上下文信息、堆栈指针(初始指针)、最后错误信息、线程的消息队列等。注意核心线程对象与核心线程管理对象的区别。下面是核心线程对象的定义。
BEGIN_DEFINE_OBJECT(__KERNEL_THREAD_OBJECT) INHERIT_FROM_COMMON_OBJECT INHERIT_FROM_COMMON_SYNCHRONIZATION_OBJECT __KERNEL_THREAD_CONTEXT KernelThreadContext; DWORD dwThreadID; DWORD dwThreadStatus; __PRIORITY_QUEUE* lpWaitingQueue; DWORD dwThreadPriority; DWORD dwScheduleCounter; DWORD dwReturnValue; DWORD dwTotalRunTime; DWORD dwTotalMemSize; __KERNEL_FILE* lpCurrentDirectory; __KERNEL_FILE* lpRootDirectory; __KERNEL_FILE* lpModuleDirectory; __COMMON_OBJECT* StartTime; BOOL bUsedMath; DWORD dwStackSize; LPVOID lpInitStackPointer; DWORD (*KernelThreadRoutine)(LPVOID); LPVOID lpRoutineParam; //The following four members are used to manage the message queue //of the current kernel thread. __KERNEL_THREAD_MESSAGE KernelThreadMsg[MAX_KTHREAD_ MSG_NUM]; UCHAR ucMsgQueueHeader; UCHAR ucMsgQueueTrial; UCHAR ucCurrentMsgNum; UCHAR ucAligment; __EVENT* lpMsgEvent; DWORD dwLastError; __KERNEL_THREAD_OBJECT_TABLE KernelObjectTable; //Object's table. __KERNEL_THREAD_OBJECT* lpParentKernelThread; END_DEFINE_OBJECT()
为了版面上的清晰,省略了源代码中的相关注释。该对象用于管理每个核心线程,因此该对象的成员变量包含了核心线程相关的方方面面。表4-4中,按照变量的用途进行归类,并对每个变量的含义进行了解释。
表4-4 核心线程对象各成员的含义
下面对线程对象的上述类别信息进行粗略的描述,详细的描述,请参考本章后续章节。
(1)硬件上下文:这是一个__KERNEL_THREAD_CONTEXT类型的结构变量,用于记录线程的硬件上下文信息。所谓的硬件上下文,就是线程所运行的CPU的硬件寄存器,比如指令指针寄存器、堆栈寄存器等,这些寄存器信息在线程切换的时候需要保存或恢复。在线程从运行状态切换到其他状态(比如,就绪状态)的时候,调度程序就会把当前线程所运行的CPU的寄存器信息保存到这个数据结构中,在线程被再次调度运行的时候,调度程序从这个数据结构中,恢复对应的寄存器信息。需要注意的是,这个结构的定义是CPU特定的,即不同的CPU,该结构的定义也不一样。详细情况请参考4.2.4节。
(2)线程属性信息:包含了线程ID、线程的当前状态、线程的返回值(线程函数的返回值)以及线程的最后错误信息等内容。
(3)堆栈信息:堆栈是线程运行过程中,保存临时数据和临时变量的地方,在核心线程对象中,记录了线程堆栈的大小(dwStackSize)和线程堆栈的初始地址(lpInitStackPointer)。需要注意的是,lpInitStackPointer不是线程的堆栈指针,线程的堆栈指针在线程的运行过程中不断变化。
(4)消息队列信息:每个线程都有一个本地消息队列,用于存储别的线程(或者自己)发过来的消息,在Hello China的当前实现中,消息队列是一个环行队列,ucMsgQueueHeader和ucMsgQueueTrial两个变量记录了唤醒队列的头和尾,ucCurrentMsgNum则记录了当前队列中的消息数目。线程采用GetMessage函数从线程队列中获取信息,别的线程采用SendMessage函数给一个特定的线程发送信息。
(5)同步信息:与其他同步对象(比如Event、Mutex等)一样,核心线程对象也是一个同步对象,所不同的是,核心线程对象的状态不能人为地通过API函数控制,而只能根据线程的运行状态来自行控制。一个线程对象只有其状态成为Terminal(KERNEL_THREAD_STATUS_TERMINAL)的时候,才是发信号状态(可用状态),所有其他状态都为不可用状态。比如,另外一个线程(假设为A)调用WaitForThisObject函数,等待一个线程对象(假设为B),则该线程A将一直处于阻塞状态(被放入线程B的lpWaitingQueue),直到线程B运行完毕,状态变化为Terminal的时候,线程A才会被唤醒。
(6)调度信息:包含两个变量dwScheduleCounter和dwThreadPriority,这两个变量是线程调度策略的依据。
(7)资源占用信息:描述了线程的资源占用情况,比如创建的核心对象、占用的物理内存大小、占用的CPU时间(运行时间)、是否使用了数学协处理器等。
(8)线程函数信息:线程的功能函数和其参数,线程函数是实现线程功能的主体,由应用程序编写。线程函数的参数是一个无类型指针(LPVOID),可以通过该指针传递任何参数信息。