avatar

Catalog
时间片管理与备用线程

前一篇,我们主要分析了线程切换的几种情况,其中一种是时钟中断,但并不是说只要有时钟中断就一定会切换线程,时钟中断时,两种情况会导致线程切换:

  • 当前线程的CPU时间片到期
  • 存在备用线程(KPCR.PrcbData.NextThread处值不为空)

CPU时间片

ThreadQuantum

当一个新的线程开始执行时,初始化程序会在KTHREAD.Quantum赋初始值,该值的大小由KPROCESS.ThreadQuantum决定 随机选择一个进程查看,发现ThreadQuantum的值为6。这个值,就是该进程的线程执行时的CPU时间片。那如何使用这个值呢?我们接下来继续看。

分析KeUpdateRunTime

每次时钟中断发生时都会先调用KeUpdateRunTime函数,我们来看看这个函数干了什么事 结合两张图来看,ebx保存的是当前线程的KTHREAD,接着将当前线程的Quantum的值-3。这下就清晰一些了,前面说了,一个线程初始的Quantum值为6,这里将Quantum的值 减了3。然后还做了什么呢,根据上面的信息,可以得知eax保存的KPCR,下面有一个逻辑判断,如果减3后值不为0,这里程序就跳转了,但是如果为0,此时程序会给KPCR+0x9AC处(QuantumEnd)的值赋上一个不为0的值,这个操作有什么用呢?往后看就知道了。

分析KiDispatchInterrupt

这里小盆友可能会奇怪了,为什么突然就从KeUpdateRunTime就跳到这了呢?这里需要说明一下,KeUpdateRumTime函数,是每次时钟中断发生时都会调用的函数,这个函数做了两件事:

  1. 将当前线程的KTHREAD.Quantum的值减3
  2. 若Quantum的值减到了0,则会将KPCR.QuantumEnd的值置为一个不为0的数

之后这个函数就执行完了,接着,就是我们上一篇分析过的,时钟中断的执行流程,最终,在进行线程切换之前,会执行到KiDispatchInterrupt函数,接下来,就来看看刚刚修改过的两个值,和这个函数有何关系:

  1. 进入KiDispatchInterrupt函数,这里有一个判断,稍作分析 这里的ebx存着的是KPCR,然后程序会去判断KPCR.QuantumEnd处的值是否为0,如果不是0,说明时间片走完了,也就是KTHREAD.Quantum值被减为0了,这是就会进行跳转,图中会跳转到loc_404902的位置
  2. 跟到loc_404902的位置继续观察, 这里先将KPCR.QuantumEnd的值置零,然后跳转到KiQuantumEnd函数中继续执行(为什么先赋值,再清零呢?因为已经判断过了,已经跳转到这里了,将QuantumEnd的值置零,也是为了下一个执行的线程)
  3. 好,进入KiQuantumEnd函数 具体细节看图,这部分,主要是重新设置了当前这个线程的CPU时间片的值为ThreadQuantum。接着往下看 然后这个函数调用了KiFindReadyThread函数,在就绪队列中找到一个线程,接着就返回了
  4. 执行完KiQuantumEnd函数后,我们又回到了KiDispatchInterrupt函数 如果刚刚在KiFindReadyThread可以在就绪队列中找到一个线程,那么eax的值就不为空,如图,接下来会跳转到loc_4048BB的位置
  5. 接着看4048BB的位置 看到了我们熟悉的线程切换函数

小结

通过分析KeUpdateRunTime和KiDispatchInterrupt函数,我们可以发现。在CPU时间片用完的情况下,当时钟中断发生时,会发生线程的切换,这里做个小结:

  • 当一个新的线程开始执行时,初始化程序会在KTHREAD.Quantum赋初始值,该值的大小由KPRCOESS.ThreadQuantum决定
  • 每次时钟中断会调用KeUpdateRunTime函数,该函数每次将当前线程Quantum减少3个单,如果减到0,则将KPCR.PrcbData.QuantumEnd的值设置为非0
  • KiDispatchInterrupt判断时间片到期后调用KiQuantumEnd函数(重新设置时间片、找到要运行的线程)

备用线程

这里我们直接定位到KiDispatchInterrupt的位置,看图 可以发现,这是刚刚判断时间片是否到期的位置,这里共有两个判断:

  1. 若CPU时间片到期(即KPCR.QuantumEnd的值为非0),则跳转,否则继续执行
  2. 若存在备用线程,则将备用线程取出。啥是备用线程呢?就是KPCR+0x128的位置,该处成员名称叫做NextThread,是一个KTHREAD结构。如果这个位置的值不为0,那么程序会继续执行

后面的事情,看图片也就知道了。取出备用线程后,会先将当前线程放入就绪链表。这里为什么不放入等待链表呢?因为该线程处于就绪状态,只是在时钟中断发生时CPU时间片走完了或者存在备用线程,所以不会放入等待链表中。

当然。如果两个判断都没有执行,程序会直接跳到最后,返回了,也就是不发生线程切换

总结

  1. 当前线程主动调用API:

    KiSwapThread -> KiSwapContext -> SwapContext

  2. 当前线程时间片到期
    KiDispatchInterrupt -> KiQuantumEnd -> SwapContext

  3. 存在备用线程
    KiDispatchInterrupt -> SwapContext

参考教程:https://www.bilibili.com/video/BV1NJ411M7aE?p=50

参考文章:

  1. https://blog.csdn.net/qq_41988448/article/details/103421772
  2. https://blog.csdn.net/qq_38474570/article/details/104273704
Author: cataLoc
Link: http://cata1oc.github.io/2020/04/02/%E6%97%B6%E9%97%B4%E7%89%87%E7%AE%A1%E7%90%86/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
Donate
  • 微信
    微信
  • 支付寶
    支付寶