简介
代码是从ReactOS里扒拉出来的,简单概括一下本文内容:线程调度的工作就是切换核心结构里的线程结构,切换内核栈,切换CR3
-
KiSetPriorityThread函数设置线程优先级,主要就是线程在running状态的时候进行设置,设置完之后会给kprcb填充下一个要执行的线程
-
KiIdleLoop函数检测Kprcb里下一个线程有值,有值就把下一个线程设置为当前线程,并进行调用KiSwapContext函数
-
KiSwapContext函数保存ebp、esi、edi,ebx寄存器,然后调用KiSwapContextInternal函数
-
KiSwapContextInternal函数预留出栈中8字节空间,把内核栈esp给到第一个参数里,跳转到KiSwapContextEntry函数
-
KiSwapContextEntry函数保存老线程的APC、异常链表、内核栈esp等信息到SwitchFrame结构,然后存到老线程的内核栈里,调用KiSwitchThreads进行切换
-
KiSwitchThreads函数切换新线程的内核栈esp,然后调用KiSwapContextExit函数
-
KiSwapContextExit函数切换cr3到新线程,把新线程的switchFrame结构还原到线程结构里,判断APC、DPC之后就返回了
切换线程结构的同时也就把线程上下文一起给切换了,线程上下文的各种信息,都是从Kprcb的CurrentThread的CONTEXT和TRAP_FRAME中获取的,所以只要切换了Kprcb的CurrentThread,也就同时给切换了
KiSetPriorityThread
这里主要关注running状态的线程被设置的处理:
- 获取下一个核心,拿到kprcb结构
- 如果运行的核心没变(下一次还在当前核心运行)
- 保存老的优先级,设置新的优先级
- 如果新的优先级比老的小,且kprcb里没有下一个线程,则去找一个就绪的线程
- 设置新线程的状态,并设置为kprcb的下一个线程
VOID
FASTCALL
KiSetPriorityThread(IN PKTHREAD Thread,
IN KPRIORITY Priority)
{
PKPRCB Prcb;
ULONG Processor;
BOOLEAN RequestInterrupt = FALSE;
KPRIORITY OldPriority;
PKTHREAD NewThread;
ASSERT((Priority >= 0) && (Priority <= HIGH_PRIORITY));
/* Check if priority changed */
// 设置优先级
if (Thread->Priority != Priority)
{
/* Loop priority setting in case we need to start over */
for (;;)
{
...
...
else if (Thread->State == Running)
{
/* Get the PRCB for the thread and lock it */
// 获取Kprcb
Processor = Thread->NextProcessor; // 取出下一个核心
Prcb = KiProcessorBlock[Processor]; // 取出核心的Kprcb
KiAcquirePrcbLock(Prcb);
/* Check if we're still the current thread running */
// 判断当前线程和目标线程Kprcb(每个核心都有一个)是否相等
if (Thread == Prcb->CurrentThread)
{
/* Get the old priority and update ours */
// 保存老的优先级
OldPriority = Thread->Priority;
// 设置新的优先级
Thread->Priority = (SCHAR)Priority;
/* Check if there was a change and there's no new thread */
// 小于老的优先级,且kprck里没有下一个线程
if ((Priority < OldPriority) && !(Prcb->NextThread))
{
/* Find a new thread */
// 寻找新的线程,先从就绪链表里找
NewThread = KiSelectReadyThread(Priority + 1, Prcb);
if (NewThread)
{
/* Found a new one, set it on standby */
// 设置新的线程状态
NewThread->State = Standby;
// 设置Kprcb的下一个线程为新线程
Prcb->NextThread = NewThread;
/* Request an interrupt */
// 请求中断
RequestInterrupt = TRUE;
}
}
/* Release the lock and check if we need an interrupt */
KiReleasePrcbLock(Prcb);
if (RequestInterrupt)
{
/* Check if we're running on another CPU */
if (KeGetCurrentProcessorNumber() != Processor)
{
/* We are, send an IPI */
KiIpiSend(AFFINITY_MASK(Processor), IPI_DPC);
}
}
}
else
{
/* Thread changed, release lock and restart */
KiReleasePrcbLock(Prcb);
continue;
}
}
...
/* If we got here, then thread state was consistent, so bail out */
break;
}
}
}
KiIdleLoop
检查kprcb里的下一个线程,来调度执行:
- 当kprcb里有下一个线程了
- 获取当前线程结构和新线程结构
- 将kprcb里的当前线程设置为下一个线程,下一个线程设置为空
- 设置新线程状态为Running
- 切换IRQL等级,调用KiSwapContext函数(下面会分析)
VOID
FASTCALL
KiIdleLoop(VOID)
{
// 获取Kprcb
PKPRCB Prcb = KeGetCurrentPrcb();
// 线程结构
PKTHREAD OldThread, NewThread;
/* Initialize the idle loop: disable interrupts */
_enable();
YieldProcessor();
YieldProcessor();
_disable();
/* Now loop forever */
while (TRUE)// 无限循环
{
/* Check for pending timers, pending DPCs, or pending ready threads */
if ((Prcb->DpcData[0].DpcQueueDepth) || //挂起定时器
(Prcb->TimerRequest) || // 定时器
(Prcb->DeferredReadyListHead.Next)) // 就绪链表
{
/* Quiesce the DPC software interrupt */
// 清空一个软中断
HalClearSoftwareInterrupt(DISPATCH_LEVEL);
/* Handle it */
// 处理
KiRetireDpcList(Prcb);
}
/* Check if a new thread is scheduled for execution */
// 检查新的线程需要调度执行
if (Prcb->NextThread)
{
/* Enable interupts */
// 开中断
_enable();
/* Capture current thread data */
// 获取当前线程数据
OldThread = Prcb->CurrentThread;
NewThread = Prcb->NextThread;
/* Set new thread data */
// 设置新的线程,线程切换
Prcb->NextThread = NULL;
Prcb->CurrentThread = NewThread;
/* The thread is now running */
// 新的线程设置为运行
NewThread->State = Running;
/* Do the swap at SYNCH_LEVEL */
// IRQL等级切换
KfRaiseIrql(SYNCH_LEVEL);
/* Switch away from the idle thread */
// 从idle线程切换出来
KiSwapContext(APC_LEVEL, OldThread);
/* Go back to DISPATCH_LEVEL */
// 设置IRLQL等级
KeLowerIrql(DISPATCH_LEVEL);
/* We are back in the idle thread -- disable interrupts again */
_enable();
YieldProcessor();
YieldProcessor();
_disable();
}
else
{
/* Continue staying idle. Note the HAL returns with interrupts on */
// 把代码停留再IDLE线程里
Prcb->PowerState.IdleFunction(&Prcb->PowerState);
}
}
}
KiSwapContext
保存寄存器环境,调用KiSwapContextInternal函数(下面会分析)
BOOLEAN
_declspec(naked)
FASTCALL
KiSwapContext(
IN KIRQL WaitIrql,
IN PKTHREAD CurrentThread
){ _asm{
//PUBLIC @KiSwapContext@8
//@KiSwapContext@8:
/* Save 4 registers */
sub esp, 4 * 4
/* Save all the non-volatile ones */
// 寄存器保存
mov [esp+12], ebx
mov [esp+8], esi
mov [esp+4], edi
mov [esp+0], ebp
/* Get the wait IRQL */
// 获取等待IRQL
or dl, cl
/* Do the swap with the registers correctly setup */
// 调用API进行内部交换
call KiSwapContextInternal//@0
/* Return the registers */
// 还原寄存器
mov ebp, [esp+0]
mov edi, [esp+4]
mov esi, [esp+8]
mov ebx, [esp+12]
/* Clean stack */
add esp, 4 * 4
ret
}}
KiSwapContextInternal
将esp保存到了第一个参数里,跳转到了KiSwapContextEntry
_declspec(naked) KiSwapContextInternal(){ _asm{
//PUBLIC @KiSwapContextInternal@0
//@KiSwapContextInternal@0:
/* Build switch frame */
// 在某个函数最后面进行了设置
sub esp, 2 * 4
mov ecx, esp // esp存到第一个参数里了
jmp KiSwapContextEntry//@8
}}
KiSwapContextEntry
保存老线程的APC、异常链表、内核栈等信息到SwitchFrame结构,然后存到老线程的内核栈里,调用KiSwitchThreads进行切换
- 保存了SwitchFrame的APC和异常链表
- 自增上下文切换总数
- 拿到新的和老的线程结构
- 将老的线程内核栈设置为SwitchFrame
- 调用KiSwitchThreads进行线程切换
typedef struct _KSWITCHFRAME
{
PVOID ExceptionList;
BOOLEAN ApcBypassDisable;
PVOID RetAddr;
} KSWITCHFRAME, *PKSWITCHFRAME;
VOID
FASTCALL
KiSwapContextEntry(IN PKSWITCHFRAME SwitchFrame,
IN ULONG_PTR OldThreadAndApcFlag)
{
PKIPCR Pcr = (PKIPCR)KeGetPcr();
PKTHREAD OldThread, NewThread;
/* Save APC bypass disable */
// 保存了SwitchFrame的APC和异常链表
SwitchFrame->ApcBypassDisable = OldThreadAndApcFlag & 3;
SwitchFrame->ExceptionList = Pcr->NtTib.ExceptionList;
/* Increase context switch count and check if tracing is enabled */
// 自增上下文切换总数
Pcr->ContextSwitches++;
if (Pcr->PerfGlobalGroupMask)
{
/* We don't support this yet on x86 either */
DPRINT1("WMI Tracing not supported\n");
ASSERT(FALSE);
}
/* Get thread pointers */
// 拿到新的老的线程
OldThread = (PKTHREAD)(OldThreadAndApcFlag & ~3);
NewThread = Pcr->PrcbData.CurrentThread;
/* Get the old thread and set its kernel stack */
// 老线程的内核栈 = SwitchFrame 建立旧的线程的内核栈
OldThread->KernelStack = SwitchFrame;
/* Do the switch */
// 进行切换
KiSwitchThreads(OldThread, NewThread->KernelStack);
}
KiSwitchThreads
切换内核栈esp,然后调用KiSwapContextExit
VOID
_declspec(naked)
FASTCALL
KiSwitchThreads(
IN PKTHREAD OldThread,
IN PKTHREAD NewThread
){ _asm{
//PUBLIC @KiSwitchThreads@8
//@KiSwitchThreads@8:
/* Load the new kernel stack and switch OS to new thread */
// 加载新的内核栈
mov esp, edx // 切换内核栈,KernelStack是线程切换的时候用来保存esp的
call KiSwapContextExit//@8
/* Now we're on the new thread. Return to the caller to restore registers */
add esp, 2 * 4
ret
}}
KiSwapContextExit
做最后的切换:页表切换
- 老线程是参数传入的,新线程是kprcb获取的
- 根据线程获取进程,判断是否是同进程切换
- 非同进程线程切换,切换新的页表,同进程线程切换则不进行操作
- 新线程的上下文切换次数自增
- 加载异常链表到Kpcr
- 判断DPC是否有效,无效就蓝屏
- 判断APC是否启用,启用就返回TRUE
BOOLEAN
FASTCALL
KiSwapContextExit(IN PKTHREAD OldThread,
IN PKSWITCHFRAME SwitchFrame)
{
PKIPCR Pcr = (PKIPCR)KeGetPcr();
PKPROCESS OldProcess, NewProcess; // 两个进程
PKTHREAD NewThread; // 新的线程
ARM_TTB_REGISTER TtbRegister;
/* We are on the new thread stack now */
// 获取新线程
NewThread = Pcr->PrcbData.CurrentThread;
/* Now we are the new thread. Check if it's in a new process */
// 获取新老进程,可能是同进程切换,也可能使非同进程切换
OldProcess = OldThread->ApcState.Process;
NewProcess = NewThread->ApcState.Process;
if (OldProcess != NewProcess)// 非同进程
{
// 切换cr3
// 获取新的页表基址
TtbRegister.AsUlong = NewProcess->DirectoryTableBase[0];
ASSERT(TtbRegister.Reserved == 0);
KeArmTranslationTableRegisterSet(TtbRegister);
}
/* Increase thread context switches */
// 新线程的上下文切换次数自增
NewThread->ContextSwitches++;
/* Load data from switch frame */
// 加载异常链表到Kpcr
Pcr->NtTib.ExceptionList = SwitchFrame->ExceptionList;
/* DPCs shouldn't be active */
// 判断DPC是否有效
if (Pcr->PrcbData.DpcRoutineActive)
{
// 蓝屏函数
/* Crash the machine */
KeBugCheckEx(ATTEMPTED_SWITCH_FROM_DPC,
(ULONG_PTR)OldThread,
(ULONG_PTR)NewThread,
(ULONG_PTR)OldThread->InitialStack,
0);
}
// 判断APC状态
/* Kernel APCs may be pending */
if (NewThread->ApcState.KernelApcPending)
{
// APC是否开启
/* Are APCs enabled? */
if (!NewThread->SpecialApcDisable)
{
// 发起APC执行
/* Request APC delivery */
if (SwitchFrame->ApcBypassDisable) HalRequestSoftwareInterrupt(APC_LEVEL);
return TRUE;
}
}
/* Return */
return FALSE;
}