日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

进程线程004 Windows线程切换的三种方式

發(fā)布時間:2025/3/21 windows 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 进程线程004 Windows线程切换的三种方式 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

文章目錄

    • 主動切換(調(diào)用API)
      • KiSwapContext函數(shù)分析
      • 哪些API調(diào)用了SwapContext函數(shù)
      • 總結(jié)
    • 時鐘中斷切換
      • 如何中斷一個正在執(zhí)行的程序
      • 系統(tǒng)時鐘
      • 時鐘中斷的執(zhí)行流程
      • 總結(jié)
    • 時間片管理
      • 1.時間片到期
        • 什么是時間片?
        • 時間片什么時候發(fā)生改變?
        • CPU時間片到期了如何處理?
        • CPU時間片總結(jié)
      • 2.存在備用線程
    • 總結(jié)

主動切換(調(diào)用API)

之前我們已經(jīng)學習了模擬Windows線程切換的代碼,里面用于線程切換的函數(shù)就是SwitchContext。只要調(diào)用這個函數(shù)就會導(dǎo)致線程切換,Windows也有類似的函數(shù):KiSwapContext

KiSwapContext函數(shù)分析

用IDA打開ntkrlpa.exe,找到KiSwapContext函數(shù)

.text:004699B4 sub esp, 10h .text:004699B7 mov [esp+10h+var_4], ebx ; ------------------------ .text:004699BB mov [esp+10h+var_8], esi .text:004699BF mov [esp+10h+var_C], edi ; 保存當前線程的寄存器現(xiàn)場 .text:004699C3 mov [esp+10h+var_10], ebp ; ---------------------------

首先,KiSwapContext保存當前線程的寄存器現(xiàn)場

.text:004699C6 mov ebx, ds:0FFDFF01Ch ; 取出KPCR存到ebx

接著取出KPCR存到ebx

.text:004699CC mov esi, ecx ; ecx是上一層調(diào)用的函數(shù)傳進來的 是要切換線程的KTHREAD

這個ecx來自上一層函數(shù)的傳參

Ctrl+X找到上一層調(diào)用

ecx來自于eax,而eax是KiFindReadyThread函數(shù)的返回值,該函數(shù)會返回一個就緒線程的KTHREAD

.text:004699C6 mov ebx, ds:0FFDFF01Ch ; 取出KPCR存到ebx .text:004699CC mov esi, ecx ; 要切換線程的KTHREAD .text:004699CE mov edi, [ebx+124h] ; KPCR+0x124是當前線程的KTHREAD結(jié)構(gòu)體

回到KiSwapContext函數(shù),此時esi存儲的是要切換線程的KTHREAD,edi是當前線程的KTHREAD。

.text:004699D4 mov [ebx+124h], esi ; 修改KPCR里的當前線程為目標線程

接著修改KPCR里的當前線程為目標線程

.text:004699DA mov cl, [edi+58h] .text:004699DD call SwapContext ; 進行線程切換

接著調(diào)用SwapContext函數(shù)進行線程切換,跟進SwapContext函數(shù)。這個函數(shù)的代碼比較復(fù)雜,先來看幾個關(guān)鍵代碼

這行代碼將當前的ESP存儲到KernelStack里,繼續(xù)往下找到另外一行關(guān)鍵代碼

這行代碼將目標線程的KernelStack存到ESP里。真正的線程切換從這里開始,從這行代碼往后已經(jīng)不再是當前線程了,而是目標線程的堆棧。

哪些API調(diào)用了SwapContext函數(shù)

現(xiàn)在我們知道了只要調(diào)用了SwapContext就會導(dǎo)致線程切換,那么現(xiàn)在我們可以看一下到底有多少個API調(diào)用了這個函數(shù)

先找到KiSwapContext的函數(shù)KiSwapThread

打開交叉引用,可以看到有7個函數(shù)都調(diào)用了KiSwapThread。那就意味著只要我們調(diào)用了這里面的任何一個函數(shù)都會導(dǎo)致線程切換。

再來查看一下其中一個父函數(shù)KeWaitForSingleObject,看看這個函數(shù)被多少個函數(shù)調(diào)用

總共270個函數(shù)調(diào)用了父函數(shù)KeWaitForSingleObject,還有6個父函數(shù)我們沒有查看。這270個函數(shù)如果再被其他函數(shù)調(diào)用也會導(dǎo)致線程切換

這樣我們可以得出一個結(jié)論,絕大多數(shù)的內(nèi)核函數(shù)都會調(diào)用SwapContext,導(dǎo)致線程切換

總結(jié)

  • Windows中絕大部分的API都會調(diào)用SwapContext函數(shù),也就是說,當線程調(diào)用了API就會導(dǎo)致線程切換
  • 時鐘中斷切換

    那么如果當前的線程不去調(diào)用系統(tǒng)API,操作系統(tǒng)是不是就無法實現(xiàn)線程切換了呢?實際上并不是這樣?我們先要來分析一下如何中斷一個正在執(zhí)行的程序

    如何中斷一個正在執(zhí)行的程序

  • 異常 比如缺頁或者INT N指令
  • 中斷 比如時鐘中斷
  • 系統(tǒng)時鐘

    IDT表中斷號IRQ說明
    0x30IRQ0時鐘中斷

    Windows系列的操作系統(tǒng)每隔10-20毫秒會觸發(fā)一次時鐘中斷。如果要獲取當前系統(tǒng)的時鐘中斷間隔,可使用W32 API:GetSystemTimeAdjustment

    時鐘中斷的執(zhí)行流程

    接下來分析時鐘中斷的執(zhí)行流程

    用IDA打開ntkrnlpa.exe,搜索_IDT

    找到中斷號為0x30的中斷處理函數(shù),只要分析這個函數(shù),就能知道系統(tǒng)的執(zhí)行流程

    這里調(diào)用了一個非當前模塊的函數(shù)

    在導(dǎo)入表中我們可以看到這個函數(shù)來自于HAL

    然后又調(diào)用了一個HAL模塊中的函數(shù)。

    繼續(xù)跟進,用IDA打開hal.dll,找到HalBeginSystemInterrupt函數(shù)。這個函數(shù)并沒有回到ntkrnlpa.exe

    再往下找到HalEnableSystemInterrupt函數(shù)

    這個函數(shù)在內(nèi)部又調(diào)用了KiDispatchInterrupt

    接著搜索導(dǎo)入表,可以看到這個函數(shù)來自于ntoskrl內(nèi)核文件

    繼續(xù)在ntoskrl跟進KiDispatchInterrupt函數(shù)

    這個函數(shù)里面也調(diào)用了SwapContext。到這里,大致的流程也就分析完成了。這說明當時鐘中斷發(fā)生的時候,也會觸發(fā)線程切換

    時鐘中斷的執(zhí)行流程:

  • KiStartUnexpectedRange
  • KiEndUnexpectedRange
  • KiUnexpectedInterruptTail
  • HalEndSystemInterrupt
  • KiDispatchInterrupt
  • SwapContext
  • 總結(jié)

    線程切換的幾種情況

  • 主動調(diào)用API函數(shù)
  • 時鐘中斷
  • 異常處理
  • 如果一個線程不調(diào)用API,在代碼中屏蔽中斷,并且不會出現(xiàn)異常,那么當前線程將永久占有CPU。單核占有率100%,2核就是50%

    時間片管理

    我們已經(jīng)知道時鐘中斷會導(dǎo)致線程進行切換,但并不是說只要有時鐘中斷就一定會切換線程,時鐘中斷時,兩種情況會導(dǎo)致線程切換

  • 當前線程的時間片到期
  • 存在備用線程(KPCR.PrcbData.NextThread)
  • 下面分別解釋這兩種情況

    1.時間片到期

    什么是時間片?

    當一個新的線程開始執(zhí)行的時候,初始化程序會在_KTHREAD.Quantum賦初始值,該值的大小由_KPROCESS.ThreadQuantum決定

    我們在winbdg中隨便查看一個進程結(jié)構(gòu)體

    kd> dt _KPROCESS 889e0288 ntdll!_KPROCESS+0x000 Header : _DISPATCHER_HEADER+0x010 ProfileListHead : _LIST_ENTRY [ 0x889e0298 - 0x889e0298 ]+0x018 DirectoryTableBase : 0x7ea1d520+0x01c LdtDescriptor : _KGDTENTRY+0x024 Int21Descriptor : _KIDTENTRY+0x02c ThreadListHead : _LIST_ENTRY [ 0x889e1960 - 0x88846e58 ]+0x034 ProcessLock : 0+0x038 Affinity : _KAFFINITY_EX+0x044 ReadyListHead : _LIST_ENTRY [ 0x889e02cc - 0x889e02cc ]+0x04c SwapListEntry : _SINGLE_LIST_ENTRY+0x050 ActiveProcessors : _KAFFINITY_EX+0x05c AutoAlignment : 0y0+0x05c DisableBoost : 0y0+0x05c DisableQuantum : 0y0+0x05c ActiveGroupsMask : 0y1+0x05c ReservedFlags : 0y0000000000000000000000000000 (0)+0x05c ProcessFlags : 0n8+0x060 BasePriority : 8 ''+0x061 QuantumReset : 6 ''

    其中0x061這個位置的QuantumReset值為6。這就意味著當進程里面的線程開始執(zhí)行的時候,初始化的程序就會將QuantumReset的值拿出來存到當前線程結(jié)構(gòu)體的Quantum里。這個值就是當前線程時間片的大小

    時間片什么時候發(fā)生改變?

    每次時鐘中斷會調(diào)用KeUpdateRunTime函數(shù),該函數(shù)每次將當前線程Quantum減少3個單位,如果減到0,則將KPCR.PrcbData.QuantumEnd的值設(shè)置為非0

    在IDA中找到KeUpdateRunTime函數(shù)

    每一次時鐘中斷,都會把當前線程的CPU時間片減少3,

    接著會判斷這個值是否為0,如果為零,就會把QuantumEnd的值設(shè)置為非0,這個值是一個標志,標志著當前CPU的時間片有沒有用完。

    沒有用完的時候這個值為0,如果用完了,會存儲一個非0的值。

    CPU時間片到期了如何處理?

    KiDispatchInterrupt會判斷時間片是否到期。

    這個函數(shù)是每一次系統(tǒng)時鐘中斷最后要執(zhí)行的函數(shù)

    這行代碼會判斷當前的CPU時間片是否到期,當系統(tǒng)時間片到期后會發(fā)生跳轉(zhuǎn)

    如果時間片到期會將QuantumEnd修改為0,然后調(diào)用KiQuantumEnd函數(shù),跟進這個函數(shù)

    這個函數(shù)主要做的事情就是將CPU的時間片重新設(shè)置為ThreadQuantum,也就是最開始看的6

    設(shè)置完成之后會調(diào)用KiFindReadyThread,通過這個函數(shù)找到下一個要運行的線程。找到以后函數(shù)返回。

    也就是說KiQuantumEnd函數(shù)的作用是重設(shè)CPU時間片 找到下一個要運行的線程,接著跳轉(zhuǎn)

    跳轉(zhuǎn)以后,先調(diào)用KiReadyThread將當前線程掛到就緒鏈表里,然后調(diào)用SwapContext切換線程

    CPU時間片總結(jié)

  • 當一個新的線程開始執(zhí)行時,初始化程序會在_KTHREAD.Quantum賦初始值,該值的大小由_KPROCESS.ThreadQuantum決定(觀察ThreadQuantum大小)
  • ? 每次時鐘中斷會調(diào)用KeUpdateRunTime函數(shù),該函數(shù)每次將當前線程Quantum減少3個單位,如果減到0,則將KPCR.PrcbData.QuantumEnd的值設(shè)置為非0
  • KiDispatchInterrupt判斷時間片到期后,調(diào)用KiQuantumEnd(重新設(shè)置時間片、找到要運行的線程)
  • 2.存在備用線程

    接著回到KiDispatchInterrupt函數(shù),這里首先會判斷CPU時間片是否到期,接著判斷備用線程是否為0,如果在不為0有備用線程的前提下,繼續(xù)往下執(zhí)行

    同樣會調(diào)用SwapContext函數(shù)進行線程切換

    如果以上兩個條件都不滿足,代碼會進行跳轉(zhuǎn),函數(shù)直接retn返回,此時不會發(fā)生線程切換

    總結(jié)

    線程切換的三種情況

  • 當前線程主動調(diào)用API:API函數(shù)–>KiSwapThread–>KiSwapContext–>SwapContext
  • 當前線程的時間片到期:KiDispatchInterrrupt–>KiQuantumEnd–>KiSwapContext–>SwapContext
  • 有備用線程:KiDispatchInterrrupt–>SwapContext
  • 如果時鐘中斷的時候時間片沒有到期且沒有備用線程,那么函數(shù)會直接返回,不會發(fā)生線程切換
  • 總結(jié)

    以上是生活随笔為你收集整理的进程线程004 Windows线程切换的三种方式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。