APC的内部实现
今天心情如同死去的病人的心电图一般,直线一条。所以没啥好说的。 天津泰达拿了亚军,还是挺给力的,下赛季去球场看亚冠,多少年没去现场看球了,上次去还是泰达球场的落成仪式,泰达2:1墨尔本,虽然中国足球没什么好看的,但是球场是一个比较便宜的可以宣泄情绪的地方.... 高中毕业时图便宜买的4k的电脑,现在发现不够用了,VS开一个大工程,再开虚拟机 word qq什么的,就有点慢了。。后悔啊后悔 APC比较简单,一看便知,一篇文就搞定~有累代码了,着色就一篇发不了这么多字了,头痛~ =========================================================================== 一个经典的投放apc结束线程是这样的KeInitializeApc(exitAPC,ethread,OriginalApcEnvironment,KernelKillThreadRoutine,0,0,KernelMode,0); KeInsertQueueApc(exitAPC,exitAPC,NULL,2); 1.APC结构 typedef struct _KAPC { UCHAR Type; UCHAR SpareByte0; UCHAR Size; UCHAR SpareByte1; ULONG SpareLong0; struct _KTHREAD *Thread; LIST_ENTRY ApcListEntry; 插入线程APC链表 PKKERNEL_ROUTINE KernelRoutine; 内核模式中执行 PKRUNDOWN_ROUTINE RundownRoutine; 线程终止时还有APC没执行会调用这个函数? PKNORMAL_ROUTINE NormalRoutine; 这个为0 表示是一个特殊内核APC,否则是一个普通的(又分为内核态的和用户态的)。特殊的位于链表前部,普通的位于后部。 普通的APC,normal和kernel例程都将被调用 PVOID NormalContext; PVOID SystemArgument1; PVOID SystemArgument2; CCHAR ApcStateIndex; APC环境状态 KPROCESSOR_MODE ApcMode; 内核态or用户态 BOOLEAN Inserted; } KAPC, *PKAPC, *PRKAPC; 2.关于APC的环境 这个枚举指的是指示线程attach之后 再回来的情形 typedef enum _KAPC_ENVIRONMENT { OriginalApcEnvironment, 原始的进程环境 AttachedApcEnvironment, 挂靠后的进程环境 CurrentApcEnvironment, 当前环境 InsertApcEnvironment 被插入时的环境 } KAPC_ENVIRONMENT; typedef struct _KAPC_STATE { LIST_ENTRY ApcListHead[MaximumMode];线程的apc链表 只有两个 内核态和用户态 struct _KPROCESS *Process; 线程 挂靠的进程 BOOLEAN KernelApcInProgress; 线程正在处理APC对象 BOOLEAN KernelApcPending; 线程有内核apc等待交付 BOOLEAN UserApcPending; 有用户态的等待 } KAPC_STATE, *PKAPC_STATE, *PRKAPC_STATE; KTHREAD 中的KAPC_STATE 中有两个 ApcState , ApcState和SaveApcState, ApcStatePointer是个数组,ApcStateIndex是下标 KiAttachProcess中//额 忘说了 已经attach的进程再次attach会BSOD... KiMoveApcState(&Thread->ApcState, SavedApcState); //当前的APC状态移到Save里,然后初始化apc状态 InitializeListHead(&Thread->ApcState.ApcListHead[KernelMode]); //ApcState被初始化 InitializeListHead(&Thread->ApcState.ApcListHead[UserMode]); Thread->ApcState.KernelApcInProgress = FALSE; Thread->ApcState.KernelApcPending = FALSE; Thread->ApcState.UserApcPending = FALSE; if (SavedApcState == &Thread->SavedApcState) { Thread->ApcStatePointer[0] = &Thread->SavedApcState; //第一个指向保存的apc状态 原始apc环境 Thread->ApcStatePointer[1] = &Thread->ApcState; //第二个是当前的 挂靠apc环境 Thread->ApcStateIndex = 1; //表示现在的状态指向 指向挂靠状态 } Dettach时,先派发APC state里面的APC,然后再恢复 //也就是在挂靠过程中线程被插apc 现在要集中解决 while (Thread->ApcState.KernelApcPending && (Thread->SpecialApcDisable == 0) && (LockHandle.OldIrql < APC_LEVEL)) { // // Unlock the thread APC lock and lower IRQL to its previous // value. An APC interrupt will immediately occur which will // result in the delivery of the kernel APC if possible. //释放这个锁将导致 请求APC级别的中断,这样apc将得到释放 KeReleaseInStackQueuedSpinLock(&LockHandle); KeAcquireInStackQueuedSpinLockRaiseToSynch(&Thread->ApcQueueLock, &LockHandle); } 省略无关代码,到这里进行恢复 KiMoveApcState(&Thread->SavedApcState, &Thread->ApcState); //恢复了 Thread->SavedApcState.Process = (PKPROCESS)NULL; Thread->ApcStatePointer[0] = &Thread->ApcState; Thread->ApcStatePointer[1] = &Thread->SavedApcState; Thread->ApcStateIndex = 0; ApcStatePointer这样设计是巧妙的 在KiInsertQueueApc中直接用下标来找当前的环境值,(那是否ApcStateIndex = 0是代表线程在自己的环境中,1在别人的环境中??) ApcState = Thread->ApcStatePointer[Apc->ApcStateIndex]; 所以如果对一个插入别人的线程插入APC,具体插入那个环境,是由APC的环境变量决定的。 0是原本的环境,1是挂靠环境。 如果目标线程挂靠了,线程原本环境是SavedApcState,挂靠环境是ApcState;没有挂靠 原本环境是ApcState,挂靠环境还是ApcState 3.APC的实现过程 VOID KeInitializeApc if (Environment == CurrentApcEnvironment) { //当前环境,那Index就是线程的 Apc->ApcStateIndex = Thread->ApcStateIndex; } else { ASSERT((Environment <= Thread->ApcStateIndex) || (Environment == InsertApcEnvironment)); Apc->ApcStateIndex = (CCHAR)Environment; } KeInsertQueueApc Thread = Apc->Thread; KeAcquireInStackQueuedSpinLockRaiseToSynch(&Thread->ApcQueueLock, &LockHandle); //升到synch_level获取apc锁 if ((Thread->ApcQueueable == FALSE) || //线程退出时不接受APC (Apc->Inserted == TRUE)) { Inserted = FALSE; } else { Apc->Inserted = TRUE; Apc->SystemArgument1 = SystemArgument1; Apc->SystemArgument2 = SystemArgument2; KiInsertQueueApc(Apc, Increment); Inserted = TRUE; } // // Unlock the thread APC queue lock, exit the scheduler, and return // whether the APC was inserted. // KeReleaseInStackQueuedSpinLockFromDpcLevel(&LockHandle); KiExitDispatcher(LockHandle.OldIrql); KiInsertQueueApc { …………. Thread = Apc->Thread; if (Apc->ApcStateIndex == InsertApcEnvironment) { //被插入线程的环境,这里面赋值 Apc->ApcStateIndex = Thread->ApcStateIndex; } ApcState = Thread->ApcStatePointer[Apc->ApcStateIndex]; ApcMode = Apc->ApcMode; ASSERT (Apc->Inserted == TRUE); if (Apc->NormalRoutine != NULL) { if ((ApcMode != KernelMode) && (Apc->KernelRoutine == PsExitSpecialApc)) {//用户模式 Thread->ApcState.UserApcPending = TRUE; InsertHeadList(&ApcState->ApcListHead[ApcMode], &Apc->ApcListEntry); } else {//普通内核模式 插入尾部 InsertTailList(&ApcState->ApcListHead[ApcMode], &Apc->ApcListEntry); } } else {//特殊内核模式 找到最后一个特殊APC 插入 始终保持特殊APC在普通的前面,又要保证插入是按照时间顺序的。 ListEntry = ApcState->ApcListHead[ApcMode].Blink; while (ListEntry != &ApcState->ApcListHead[ApcMode]) { ApcEntry = CONTAINING_RECORD(ListEntry, KAPC, ApcListEntry); if (ApcEntry->NormalRoutine == NULL) { break; } ListEntry = ListEntry->Blink; } InsertHeadList(ListEntry, &Apc->ApcListEntry); } if (Apc->ApcStateIndex == Thread->ApcStateIndex) { //是线程有的状态环境 0 or 1 原始或者挂靠的环境,另外两种状态已经在之前解决掉了 现在只有这两种。并且当现在状态相同,可以尝试让apc立即执行起来 // // If the target thread is the current thread, then the thread state // is running and cannot change. // if (Thread == KeGetCurrentThread()) {//插入的就是当前线程 ASSERT(Thread->State == Running); // // If the APC mode is kernel, then set kernel APC pending and // request an APC interrupt if special APC's are not disabled. // if (ApcMode == KernelMode) {//内核态apc 直接请求apc中断 Thread->ApcState.KernelApcPending = TRUE; if (Thread->SpecialApcDisable == 0) { KiRequestSoftwareInterrupt(APC_LEVEL); } } return; } RequestInterrupt = FALSE; KiLockDispatcherDatabaseAtSynchLevel(); if (ApcMode == KernelMode) { Thread->ApcState.KernelApcPending = TRUE; KeMemoryBarrier(); ThreadState = Thread->State; if (ThreadState == Running) {//线程正在运行 请求中断 RequestInterrupt = TRUE; } else if ((ThreadState == Waiting) && //线程处于等待状态,唤醒这个线程 在KiUnwaitThread调用 KiReadyThread 这里面会交付APC (Thread->WaitIrql == 0) && (Thread->SpecialApcDisable == 0) && ((Apc->NormalRoutine == NULL) || ((Thread->KernelApcDisable == 0) && (Thread->ApcState.KernelApcInProgress == FALSE)))) { KiUnwaitThread(Thread, STATUS_KERNEL_APC, Increment); } else if (Thread->State == GateWait) { //门等待 从门等待中拆出来 直接插入备用链表 KiAcquireThreadLock(Thread); if ((Thread->State == GateWait) && (Thread->WaitIrql == 0) && (Thread->SpecialApcDisable == 0) && ((Apc->NormalRoutine == NULL) || ((Thread->KernelApcDisable == 0) && (Thread->ApcState.KernelApcInProgress == FALSE)))) { GateObject = Thread->GateObject; KiAcquireKobjectLock(GateObject); RemoveEntryList(&Thread->WaitBlock[0].WaitListEntry); KiReleaseKobjectLock(GateObject); if ((Queue = Thread->Queue) != NULL) { Queue->CurrentCount += 1; } Thread->WaitStatus = STATUS_KERNEL_APC; KiInsertDeferredReadyList(Thread); } KiReleaseThreadLock(Thread); } } else if ((Thread->State == Waiting) && (Thread->WaitMode == UserMode) && (Thread->Alertable || Thread->ApcState.UserApcPending)) {//用户模式 正在等待 并且可以唤醒 调用 KiUnwaitThread Thread->ApcState.UserApcPending = TRUE; KiUnwaitThread(Thread, STATUS_USER_APC, Increment); } //其他的情况只能等待其他机会执行APC了 // // Unlock the dispatcher database and request an APC interrupt if // required. // //如果有请求中断 这里执行一个 KiUnlockDispatcherDatabaseFromSynchLevel(); if (RequestInterrupt == TRUE) { KiRequestApcInterrupt(Thread->NextProcessor); } } return; } 4.APC的执行过程 APC_LEVEL的中断或者中断从更高级别降下来时,apc得到机会交付。KiDeliverApc分三种不同情况,做处理,逻辑比较简单 VOID KiDeliverApc ( IN KPROCESSOR_MODE PreviousMode, IN PKEXCEPTION_FRAME ExceptionFrame, IN PKTRAP_FRAME TrapFrame ) { if (TrapFrame != NULL) { KiCheckForSListAddress(TrapFrame); } Thread = KeGetCurrentThread();//更换陷阱帧 OldTrapFrame = Thread->TrapFrame; Thread->TrapFrame = TrapFrame; Process = Thread->ApcState.Process; Thread->ApcState.KernelApcPending = FALSE; if (Thread->SpecialApcDisable == 0) { KeMemoryBarrier(); while (IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]) == FALSE) { //只处理当前线程当前状态环境下的dpc KeAcquireInStackQueuedSpinLock(&Thread->ApcQueueLock, &LockHandle); NextEntry = Thread->ApcState.ApcListHead[KernelMode].Flink; if (NextEntry == &Thread->ApcState.ApcListHead[KernelMode]) {//到这已经空了释放并退出 KeReleaseInStackQueuedSpinLock(&LockHandle); break; } // // Clear kernel APC pending, get the address of the APC object, // and determine the type of APC. // // N.B. Kernel APC pending must be cleared each time the kernel // APC queue is found to be non-empty. // Thread->ApcState.KernelApcPending = FALSE; Apc = CONTAINING_RECORD(NextEntry, KAPC, ApcListEntry); ReadForWriteAccess(Apc); KernelRoutine = Apc->KernelRoutine; NormalRoutine = Apc->NormalRoutine; NormalContext = Apc->NormalContext; SystemArgument1 = Apc->SystemArgument1; SystemArgument2 = Apc->SystemArgument2; if (NormalRoutine == (PKNORMAL_ROUTINE)NULL) {//特殊的内核历程 //调用apc RemoveEntryList(NextEntry); Apc->Inserted = FALSE; KeReleaseInStackQueuedSpinLock(&LockHandle); (KernelRoutine)(Apc, &NormalRoutine, &NormalContext, &SystemArgument1, &SystemArgument2); } else {//普通的内核apc if ((Thread->ApcState.KernelApcInProgress == FALSE) && (Thread->KernelApcDisable == 0)) { RemoveEntryList(NextEntry); Apc->Inserted = FALSE; KeReleaseInStackQueuedSpinLock(&LockHandle); (KernelRoutine)(Apc, //普通的apc 比如nt!KiSuspendThread &NormalRoutine, //NormalRoutine可能会在这里被改成0,如果没改成0继续执行 &NormalContext, &SystemArgument1, &SystemArgument2); //还要调用Normal if (NormalRoutine != (PKNORMAL_ROUTINE)NULL) { //比如这可能是是nt!KiSuspendNop 啥也不干直接返回 Thread->ApcState.KernelApcInProgress = TRUE; //在这个apc执行的时候 其他的普通apc不会被交付 KeLowerIrql(0); //这里normal和kernel的irql也不一样,normal在passive运行 (NormalRoutine)(NormalContext, SystemArgument1, SystemArgument2); KeRaiseIrql(APC_LEVEL, &LockHandle.OldIrql); } Thread->ApcState.KernelApcInProgress = FALSE; } else { KeReleaseInStackQueuedSpinLock(&LockHandle); goto CheckProcess; } } } if ((PreviousMode == UserMode) && (IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]) == FALSE) && (Thread->ApcState.UserApcPending != FALSE)) { KeAcquireInStackQueuedSpinLock(&Thread->ApcQueueLock, &LockHandle); Thread->ApcState.UserApcPending = FALSE; NextEntry = Thread->ApcState.ApcListHead[UserMode].Flink; if (NextEntry == &Thread->ApcState.ApcListHead[UserMode]) { KeReleaseInStackQueuedSpinLock(&LockHandle); goto CheckProcess; } Apc = CONTAINING_RECORD(NextEntry, KAPC, ApcListEntry); ReadForWriteAccess(Apc); KernelRoutine = Apc->KernelRoutine; NormalRoutine = Apc->NormalRoutine; NormalContext = Apc->NormalContext; SystemArgument1 = Apc->SystemArgument1; SystemArgument2 = Apc->SystemArgument2; RemoveEntryList(NextEntry); Apc->Inserted = FALSE; KeReleaseInStackQueuedSpinLock(&LockHandle); (KernelRoutine)(Apc, //用户态时这里很常见的是nt!IopDeallocateApc &NormalRoutine, &NormalContext, &SystemArgument1, &SystemArgument2); if (NormalRoutine == (PKNORMAL_ROUTINE)NULL) { KeTestAlertThread(UserMode); } else {//初始化一个用户态APC,这个函数把一个KAPC_RECORD放入栈中,TrapFrame->Eip = (ULONG)KeUserApcDispatcher; 这是一个ntdll中的函数, //陷阱帧的东西还不太明白,不能说太细。。以后补充~~ KiInitializeUserApc(ExceptionFrame, TrapFrame, NormalRoutine, NormalContext, SystemArgument1, SystemArgument2); } } } ……………… 5.APC的调用时机 ★.内核代码离开临界区或守护区,调用KiCheckForKernelApcDelivery或者请求APC级别中断 ★.KiSwapThread返回以前调用一次KiDeliverApc ★.从系统服务或者异常返回时 调用KiDeliverApc 设置用户态apc的交付 ★.APC_LEVEL的中断和irql降到passive时KiDeliverApc都会被调用 By ReturnsMe http://hi.baidu.com/andriy_aolala/blog/item/5aba868b70bf036f9f2fb452.html |