在前面介绍进程结构体时,进行了断链实验,程序可以正常运行,原因是CPU执行与调度的单位是线程,因此进程的断链并不影响程序的正常执行。对线程的断链也是一样的,断链后虽然可以隐藏断掉的线程,但同样不影响程序的执行。这说明,CPU调度时根本没有用到ThreadListEntry这个链表。接下来介绍两个与调度相关的链表:等待链表&调度链表
33个链表
线程有3种状态:就绪、等待、运行
正在运行中的线程存储在KPCR中,就绪和等待的线程全在另外的33个链表中。包括1个等待链表,32个就绪链表
这些链表都使用了_KTHREAD+0x060这个位置(如果是Win7的话,位于KTHREAD+0x074的位置),也就是说,线程在某一时刻,只能属于其中一个链表内
等待链表
查询指令:dd KiWaitListHead
说明:
- 等待链表是一个双向链表
- 当线程调用了Sleep()或者WaitForSingleObject()等函数时,就挂到这个链表
关系梳理:KiWaitListHead -> WaitListEntry(KTHREAD+0x60) -> ThreadProcess(ETHREAD+0x220) -> EPROCESS
调度链表
- 查询指令:dd KiDispatcherReadyListHead L70
- 说明:
- 调度链表共32个双向链表
- 有32个调度链表的原因是,运行中的线程是有线程优先级,根据下标表示线程优先级别(0~31)
- 32位系统中,调度链表有32个(一组),64位系统则有64个(一组)
- 普通操作系统,只有一组调度链表,服务器版本系统中,调度链表的数量等于CPU的数量,但等待链表只有一个
- 结构图:
总结
- 正在运行的线程位于KPCR中
- 准备运行的线程根据线程优先级的不同,分布在32个调度链表中; KiDispatcherReadyListHead是个数组,存储了32个链表头
- 等待状态的线程存储在等待链表里,KiWaitListHead存储了链表头
- 这些圈均挂在KTHREAD+0x060的位置(XP系统)