Windows内核线程切换
2023-11-14
48
0
线程切换的函数是:KiSwapThread,该函数内部调用 KiSwapContext进行堆栈信息的切换。
ReactOS的源代码如下:
LONG_PTR FASTCALL KiSwapThread(IN PKTHREAD Thread,
IN PKPRCB Prcb
)
{
BOOLEAN ApcState = FALSE;
KIRQL WaitIrql;
LONG_PTR WaitStatus;
PKTHREAD NextThread;
ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL);
/* Acquire the PRCB lock */
KiAcquirePrcbLock(Prcb);
/* Get the next thread */
NextThread = Prcb->NextThread;
if (NextThread)
{
/* Already got a thread, set it up */
Prcb->NextThread = NULL;
Prcb->CurrentThread = NextThread;
NextThread->State = Running;
}
else
{
/* Try to find a ready thread */
NextThread = KiSelectReadyThread(0, Prcb);
if (NextThread)
{
/* Switch to it */
Prcb->CurrentThread = NextThread;
NextThread->State = Running;
}
else
{
/* Set the idle summary */
InterlockedOrSetMember(&KiIdleSummary, Prcb->SetMember);
/* Schedule the idle thread */
NextThread = Prcb->IdleThread;
Prcb->CurrentThread = NextThread;
NextThread->State = Running;
}
}
/* Sanity check and release the PRCB */
ASSERT(CurrentThread != Prcb->IdleThread);
KiReleasePrcbLock(Prcb);
/* Save the wait IRQL */
WaitIrql = CurrentThread->WaitIrql;
/* Swap contexts */
ApcState = KiSwapContext(WaitIrql, CurrentThread);
/* Get the wait status */
WaitStatus = CurrentThread->WaitStatus;
/* Check if we need to deliver APCs */
if (ApcState)
{
/* Lower to APC_LEVEL */
KeLowerIrql(APC_LEVEL);
/* Deliver APCs */
KiDeliverApc(KernelMode, NULL, NULL);
ASSERT(WaitIrql == 0);
}
/* Lower IRQL back to what it was and return the wait status */
KeLowerIrql(WaitIrql);
return WaitStatus;
}
内核中调用KiSwapThread函数的有:
- KeDelayExectureinThread
- KeWaitForSingleObject ,调用该函数的太多
- KeWaitForSingleObjects
- KeTerminateThread
- NtYieldExecution
- KiAttachProcess
线程切换时:
- 应先切换堆栈信息
- 然后通过RET可以切换RIP
线程切换总结:
- Windows中大部分API调用了SwapContext函数,也就是说,当线程调用了API,就是导致线程切换
- 线程切换时会比较是否属于同一进程,如果不是,切换CR3,CR3换了,进程也就换了。
如果一个程序不调用API,也会出现线程切换的。这是因为中断、异常会导致线程切换。
时钟中断切换导致线程切换
时钟中断函数
{
HalBeginSystemInterrupt
...
HalEdnSystemInterrupt
{
KiDispatchInterrupt
{
SwapContext
}
}
}
线程切换的几种情况:
- 主动调用API函数
- 异常处理
- 时钟中断
时钟中断会导致线程进行切换,但并不是说只要有时钟中断就一定会切换线程,时钟中断时,两种情况会导致线程切换:
1、当前的线程CPU时间片到期
2、有备用线程(KPCR.PrcbData.NextThread)
如果一个线程不调用API,在代码中断屏蔽中断(CLI指令),并且不会出现异常,那么当前线程永久占用CPU,单核占用率1005,2个核就是50%。