news 2026/4/3 4:13:34

nt!KiQuantumEnd函数分析看各个线程切换函数的作用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
nt!KiQuantumEnd函数分析看各个线程切换函数的作用

nt!KiQuantumEnd函数分析看各个线程切换函数的作用
1: kd> kc
#
00 nt!KiQuantumEnd
01 nt!KiDispatchInterrupt
02 hal!HalpDispatchInterrupt
WARNING: Frame IP not in any known module. Following frames may be wrong.
03 0x0


Thread->Quantum = Process->ThreadQuantum;


1: kd> dx -id 0,0,89831250 -r1 ((ntkrnlmp!_KTHREAD *)0x89804020)
((ntkrnlmp!_KTHREAD *)0x89804020) : 0x89804020 [Type: _KTHREAD *]
[+0x000] Header [Type: _DISPATCHER_HEADER]
[+0x010] MutantListHead [Type: _LIST_ENTRY]
[+0x018] InitialStack : 0xf75f7000 [Type: void *]
[+0x01c] StackLimit : 0xf75f4000 [Type: void *]
[+0x020] KernelStack : 0xf75f692c [Type: void *]
[+0x024] ThreadLock : 0x1 [Type: unsigned long]
[+0x028] ContextSwitches : 0x263 [Type: unsigned long]
[+0x02c] State : 0x2 [Type: unsigned char]
[+0x02d] NpxState : 0x0 [Type: unsigned char]
[+0x02e] WaitIrql : 0x0 [Type: unsigned char]
[+0x02f] WaitMode : 1 [Type: char]
[+0x030] Teb : 0x7ffd8000 [Type: void *]
[+0x034] ApcState [Type: _KAPC_STATE]
[+0x04c] ApcQueueLock : 0x0 [Type: unsigned long]
[+0x050] WaitStatus : 256 [Type: long]
[+0x054] WaitBlockList : 0x898040c0 [Type: _KWAIT_BLOCK *]
[+0x058] Alertable : 0x0 [Type: unsigned char]
[+0x059] WaitNext : 0x0 [Type: unsigned char]
[+0x05a] WaitReason : 0xd [Type: unsigned char]
[+0x05b] Priority : 15 [Type: char]

[+0x110] BasePriority : 13 '\r' [Type: char]

[+0x112] PriorityDecrement : 0 [Type: char]


Priority = Priority - Thread->PriorityDecrement - Adjustment;

1: kd> dx -id 0,0,89831250 -r1 ((ntkrnlmp!_KTHREAD *)0x89804020)
((ntkrnlmp!_KTHREAD *)0x89804020) : 0x89804020 [Type: _KTHREAD *]
[+0x000] Header [Type: _DISPATCHER_HEADER]
[+0x010] MutantListHead [Type: _LIST_ENTRY]
[+0x018] InitialStack : 0xf75f7000 [Type: void *]
[+0x01c] StackLimit : 0xf75f4000 [Type: void *]
[+0x020] KernelStack : 0xf75f692c [Type: void *]
[+0x024] ThreadLock : 0x1 [Type: unsigned long]
[+0x028] ContextSwitches : 0x263 [Type: unsigned long]
[+0x02c] State : 0x2 [Type: unsigned char]
[+0x02d] NpxState : 0x0 [Type: unsigned char]
[+0x02e] WaitIrql : 0x0 [Type: unsigned char]
[+0x02f] WaitMode : 1 [Type: char]
[+0x030] Teb : 0x7ffd8000 [Type: void *]
[+0x034] ApcState [Type: _KAPC_STATE]
[+0x04c] ApcQueueLock : 0x0 [Type: unsigned long]
[+0x050] WaitStatus : 256 [Type: long]
[+0x054] WaitBlockList : 0x898040c0 [Type: _KWAIT_BLOCK *]
[+0x058] Alertable : 0x0 [Type: unsigned char]
[+0x059] WaitNext : 0x0 [Type: unsigned char]
[+0x05a] WaitReason : 0xd [Type: unsigned char]
[+0x05b] Priority : 14 [Type: char]


if (Prcb->NextThread == NULL) {
if ((NewThread = KiSelectReadyThread(Thread->Priority, Prcb)) != NULL) {
NewThread->State = Standby;
Prcb->NextThread = NewThread;
}

} else {
Thread->Preempted = FALSE;
}

PrioritySet = KiPriorityMask[LowPriority] & Prcb->ReadySummary;


1: kd> x nt!KiPriorityMask
80a05f30 nt!KiPriorityMask = unsigned long []
80a05f30 nt!KiPriorityMask = unsigned long [32]
80a05f30 nt!KiPriorityMask = unsigned long []
1: kd> dx -r1 (*((ntkrnlmp!unsigned long (*)[32])0x80a05f30))
(*((ntkrnlmp!unsigned long (*)[32])0x80a05f30)) [Type: unsigned long [32]]
[0] : 0xffffffff [Type: unsigned long]
[1] : 0xfffffffe [Type: unsigned long]
[2] : 0xfffffffc [Type: unsigned long]
[3] : 0xfffffff8 [Type: unsigned long]
[4] : 0xfffffff0 [Type: unsigned long]
[5] : 0xffffffe0 [Type: unsigned long]
[6] : 0xffffffc0 [Type: unsigned long]
[7] : 0xffffff80 [Type: unsigned long]
[8] : 0xffffff00 [Type: unsigned long]
[9] : 0xfffffe00 [Type: unsigned long]
[10] : 0xfffffc00 [Type: unsigned long]
[11] : 0xfffff800 [Type: unsigned long]
[12] : 0xfffff000 [Type: unsigned long]
[13] : 0xffffe000 [Type: unsigned long]
[14] : 0xffffc000 [Type: unsigned long]


1111 1111 1111 1111 1100 0000 0000 0000

有比14优先级还高的就绪线程吗?


1: kd> dx -id 0,0,89831250 -r1 ((ntkrnlmp!_KPRCB *)0xf7737120)
((ntkrnlmp!_KPRCB *)0xf7737120) : 0xf7737120 [Type: _KPRCB *]
[+0x000] MinorVersion : 0x1 [Type: unsigned short]
[+0x002] MajorVersion : 0x1 [Type: unsigned short]
[+0x004] CurrentThread : 0x89804020 [Type: _KTHREAD *]
[+0x008] NextThread : 0x0 [Type: _KTHREAD *]
[+0x00c] IdleThread : 0xf7739fa0 [Type: _KTHREAD *]
[+0x010] Number : 1 [Type: char]
[+0x011] Reserved : 0 [Type: char]

[+0x928] ReadySummary : 0x200 [Type: unsigned long]


如果有的话会被选中,时间片运行完后被抢占了。

对于延迟就绪线程抢占当前线程后变成NextThread,
在KiDispatchInterrupt运行后,时间片用完了,调用nt!KiQuantumEnd被NextThread线程抢占。
时间片没用完也会发生线程切换KiSwapContext

Thread->Priority = KiComputeNewPriority(Thread, 1);
if (Prcb->NextThread == NULL) {
if ((NewThread = KiSelectReadyThread(Thread->Priority, Prcb)) != NULL) {
NewThread->State = Standby;
Prcb->NextThread = NewThread;
}

}


这里是选出相等优先级,或比当前线程优先级高的作为NextThred线程。
所以说NextThread线程比CurrentThread的优先级可能高,也可能相等!!!
如果CurrentThread和NextThread的优先级相等,则一定是KiSelectReadyThread函数中选出来的。

nt!KiQuantumEnd函数有可能调用KiSelectReadyThread函数
nt!KiSwapThread函数有可能调用KiSelectReadyThread函数
nt!KiSwapThread函数先调用KiSelectReadyThread函数选出优先级大于等于当前线程优先级的线程
如果没有则调用nt!KiFindReadyThread函数,选择就绪线程,优先级没有限制。
所以KiSelectReadyThread函数是挑选好的,符合条件的,条件是不能比自己条件还低,要门当户对。不好的不要。
而nt!KiFindReadyThread函数是线程调用nt!KiSwapThread函数自己主动放弃cpu,因为没有事可以干了,需要等待一些对象,比如事件对象。
所以nt!KiFindReadyThread函数对于优先级没有要求。只要符合基本条件就行,什么基本条件?必须得是就绪线程。

nt!KiSwapThread函数最开始还要检查是否有延迟就绪线程需要处理,如果有则先处理延迟就绪线程。

if (CurrentPrcb->DeferredReadyListHead.Next != NULL) {
KiProcessDeferredReadyList(CurrentPrcb);
}


nt!KiDeferredReadyThread函数中抢占TargetPrcb->NextThread或TargetPrcb->CurrentThread
优先级必须严格大于,没有等于,甚至还可以抢占TargetPrcb->NextThread。
TargetPrcb->NextThread被更高优先级的线程抢占后会再调用KiDeferredReadyThread(Thread1);
结果是可能抢占其他cpu的当前线程,或者放入就绪队列中。

if ((Thread1 = TargetPrcb->NextThread) != NULL) {

ASSERT(Thread1->State == Standby);

if (ThreadPriority > Thread1->Priority) {
Thread1->Preempted = TRUE;
Thread->State = Standby;
TargetPrcb->NextThread = Thread;
Thread1->State = DeferredReady;
Thread1->DeferredProcessor = CurrentPrcb->Number;
KiReleaseTwoPrcbLocks(CurrentPrcb, TargetPrcb);
KiDeferredReadyThread(Thread1);
return;
}

} else {
Thread1 = TargetPrcb->CurrentThread;
if (ThreadPriority > Thread1->Priority) {
Thread1->Preempted = TRUE;
Thread->State = Standby;
TargetPrcb->NextThread = Thread;
KiReleaseTwoPrcbLocks(CurrentPrcb, TargetPrcb);
KiRequestDispatchInterrupt(Thread->NextProcessor);
return;
}
}
这里抢占了CurrentThread后,立即请求调度中断,然后回运行nt!KiDispatchInterrupt
所以当前线程的时间片可能没用用完直接就被线程切换了。
所以nt!KiDispatchInterrupt函数中会判断时间片是否用完,
用完了调用nt!KiQuantumEnd函数,
没用完直接看看是否有NextThread线程,有的话直接线程切换。

这里为什么不像nt!KiQuantumEnd函数一样调用
KxQueueReadyThread(Thread, Prcb);
KiSwapContext(Thread, NewThread);
函数后进行线程切换,逻辑不对,
调用nt!KiQuantumEnd的线程就是当前线程,所以可以直接线程切换。
和nt!KiSwapThread函数一样,调用nt!KiSwapThread函数的线程也一定是当前线程Prcb->CurrentThread

而nt!KiDeferredReadyThread函数不一样,nt!KiDeferredReadyThread函数处理的一定不是当前线程。
Prcb->CurrentThread一定是出于running运行态。
而nt!KiDeferredReadyThread函数处理的线程处于延迟运行态=7,所以不能进行线程切换。
如果进行线程切换,会把当前正在运行的线程切换出去。所以要调用nt!KiRequestDispatchInterrupt。
nt!KiDeferredReadyThread函数处理的可能是其他cpu上的Prcb->CurrentThread
所以KiRequestDispatchInterrupt函数的参数就是哪个cpu号。
KiRequestDispatchInterrupt(Thread->NextProcessor)

下面是进行线程切换。

KiReleaseThreadLock(Thread);
if (Prcb->NextThread != NULL) {
KiSetContextSwapBusy(Thread);
NewThread = Prcb->NextThread;
Prcb->NextThread = NULL;
Prcb->CurrentThread = NewThread;
NewThread->State = Running;
Thread->WaitReason = WrQuantumEnd;
KxQueueReadyThread(Thread, Prcb);
Thread->WaitIrql = APC_LEVEL;
KiSwapContext(Thread, NewThread);

} else {
KiReleasePrcbLock(Prcb);
}


总结:如果有NextThread的话,会进行线程切换,
如果没有会调用KiSelectReadyThread,选一个优先级大于或等于当前线程的作为NextThread。
如果没有选出来NextThread,则降低优先级(也必须大于基本优先级),时间片填满后继续运行。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/27 6:37:43

Open-AutoGLM容器化部署避坑指南(99%新手都会忽略的3个细节)

第一章:Open-AutoGLM容器化部署概述Open-AutoGLM 是一个基于 AutoGLM 架构的开源自动化语言模型推理服务框架,支持灵活配置与高效部署。通过容器化技术,Open-AutoGLM 能够在不同环境中保持一致的行为表现,显著提升部署效率与可维护…

作者头像 李华
网站建设 2026/3/20 9:16:19

还在换硬件?资深工程师教你用软件调优让老电脑跑通Open-AutoGLM

第一章:老电脑也能跑Open-AutoGLM?软件调优的极限挑战在算力军备竞赛之外,如何让老旧硬件焕发新生,成为运行前沿AI模型的关键战场。Open-AutoGLM 作为一款基于大语言模型的自动化工具,通常被认为需要高性能GPU支持&…

作者头像 李华
网站建设 2026/3/31 22:11:28

为什么你的Open-AutoGLM镜像总失败?深度剖析4大常见错误根源

第一章:Open-AutoGLM镜像构建失败的典型现象 在使用Docker构建Open-AutoGLM项目镜像过程中,开发者常遇到多种典型的构建失败现象。这些异常不仅影响开发效率,还可能导致部署流程中断。以下是几种常见问题及其表现形式。 依赖包下载超时或404…

作者头像 李华
网站建设 2026/3/27 18:31:29

【企业级部署实战】:Open-AutoGLM虚拟机配置优化的7大黄金法则

第一章:Open-AutoGLM虚拟机部署方案概述Open-AutoGLM 是一个面向自动化生成式语言模型任务的开源框架,支持在虚拟化环境中快速部署与扩展。本章介绍基于虚拟机的部署方案,适用于开发测试及中小规模生产场景,提供高隔离性与灵活资源…

作者头像 李华
网站建设 2026/3/28 12:15:03

【AI模型服务化新突破】:Open-AutoGLM容器化部署的7大关键步骤

第一章:Open-AutoGLM容器化部署概述Open-AutoGLM 是一个面向自动化生成语言模型任务的开源框架,支持模型推理、微调与服务封装。通过容器化部署,开发者能够在异构环境中快速构建一致的运行时实例,提升部署效率与可维护性。容器化优…

作者头像 李华
网站建设 2026/3/28 12:26:37

自动化测试:提升效率的关键

在数字化转型加速的2025年,软件测试行业正面临着持续交付与质量保障的双重挑战。据业界数据显示,采用成熟自动化测试策略的团队,其回归测试效率比手动测试提升超过70%,缺陷逃逸率降低45%以上。自动化测试已从"锦上添花"…

作者头像 李华