线程切换时会比较是否属于同一个进程,如果不是,切换Cr3,Cr3换了,进程也就切换了
时钟中断
绝大部分系统内核函数都会调用SwapContext函数,来实现线程的切换,那么这种切换是线程主动调用的。那如果当前的线程不去调用系统API,操作系统如何实现线程切换呢?那就靠时钟中断了,这个是被动切换。可以通过中断和异常来实现中断一个正在执行的程序。其中,时钟中断也是一种中断,中断号0x30
如果一个线程不调用API,在代码中屏蔽中断(CLI指令),并且不会出现异常,那么当前线程将永久占有CPU
时间片管理
时钟中断会导致线程进行切换,但并不是说只要有时钟中断就一定会切换线程,时钟中断时,如下两种情况会导致线程切换:
- 当前的线程CPU时间片到期
- 有备用线程:KPCR.PrcbData.NextThread
时间片
- 当一个新的线程开始执行时,初始化程序会在_KTHREAD.Quantum赋初始值,该值的大小由_KPROCESS.ThreadQuantum决定
- 每次时钟中断会调用KeUpdateRunTime函数,该函数每次将当前线程Quantum减少3个单位,如果减到0,则将KPCR.PrcbData.QuantumEnd的值设置为非0
- KiDispatchInterrupt判断时间片到期,调用KiQuantumEnd重新设置时间片、找到要运行的线程
存在备用线程
这个值被设置时,即使当前线程的CPU时间片没有到期,仍然会被切换
FS
FS:[0]寄存器在3环时指向TEB,进入0环后FS:[0]指向KPCR。系统中同时存在很多个线程,这就意味着FS:[0]在3环时指向的TEB要有多个,即每个线程一份。但在实际的使用中我们发现,当我们在3环查看不同线程的FS寄存器时,FS的段选择子都是相同的,那是如何实现通过一个FS寄存器指向多个TEB呢?这一切的一切都在SwapContext这个函数里面
线程优先级
之前讲过有三种情况会导致线程切换,在KiSwapThread与KiQuantumEnd函数中都是通过KiFindReadyThread来找下一个要切换的线程,KiFindReadyThread是根据什么条件来选择下一个要执行的线程呢?
调度链表有32个,每次都从头开始查找效率太低,所以Windows都过一个DWORD类型变量的变量来记录,正好是32位,一个位代表一个链表,当向调度链表.中挂入或者摘除某个线程时,会判断当前级别的链表是否为空,为空将.变量对应位置0,否则置1,这个变量就是==_kiReadySummary==。
多CPU会随机寻找KiDispatcherReadyListHead(调度链表)指向的数组中的线程,线程可以绑定某个CPU,可以使用API:SetThreadAffinityMask进行设置。
如果没有就绪线程怎么办?CPU是不可能闲下来的,它会执行一个空闲线程,即为IdleThread