selph
selph
发布于 2021-11-15 / 510 阅读
0
0

APC插入过程--基于Windows XP源码

APC插入:KeInsertQueueApc(位于:ntoskrnl/ke/apc.c):

  • 判断Apc是否可插入:线程是否可插入,Apc是否已插入
    • 如果可插入,就把参数设置给Apc,修改Apc插入标识,调用KiInsertQueueApc
    • 如果不可插入,设置返回值为FALSE

APC插入:KiInsertQueueApc(位于:ntoskrnl/ke/apc.c):

  • 如果有NormalRoutine,说明是普通APC
    • 如果是用户模式的退出线程APC,就插入到APC链表首位
    • 如果是普通APC(用户模式或内核模式),就插入到对应APC链表最后
  • 如果没NormalRoutine,说明是特殊内核APC
    • 找到内核APC链表中特殊APC部分的最后一个成员,插入(插入到特殊APC队列的最后)
  • 检查APC状态索引是否和线程匹配,一般是匹配的
  • 如果APC归属线程是当前线程,且线程正在运行
    • 如果是内核模式APC,就设置线程APC状态内核APC挂起标识
      • 如果线程没有禁用特殊APC,就调用HalRequestSoftwareInterrupt请求软中断间接调用KiDeliverApc,来执行APC。
  • 如果不是当前线程
    • 获取程序调度锁
    • 如果APC是内核模式
      • 设置内核APC挂起标识为1
      • 如果线程正在运行
        • 设置请求中断标识为1
      • 如果线程为等待状态
        • 设置状态为STATUS_KERNEL_APC,然后唤醒线程
      • 如果线程为门等待
        • 从等待链表中摘链,设置线程等待状态为STATUS_KERNEL_APC,然后调用KiInsertDeferredReadyList把线程设置为延时的就绪状态
    • 如果APC是用户模式,线程为等待状态
      • 设置用户APC挂起标识为1
      • 设置状态为STATUS_USER_APC
      • 唤醒线程
    • 释放程序调度锁
    • 请求APC中断(参数是请求中断标识)

简单来说:

如果可插入,则根据APC类型插入到对应的APC队列里,然后判断线程状态:

  • 如果当前的线程就是APC归属线程,如果是内核模式就设置标识然后请求中断,执行APC
  • 如果当前的线程不是APC归属线程
    • 如果是内核模式,线程正在运行就设置请求中断标识,最后请求APC中断,线程正在等待就设置内核APC状态并唤醒线程
    • 如果是用户模式,线程正在等待就设置用户APC状态并唤醒线程

这也说明了一点,用户APC插入后得不到立即执行,内核APC插入后会被中断执行APC

KeInsertQueueApc

/*++
 * @name KeInsertQueueApc
 * @implemented NT4
 *
 *     The KeInsertQueueApc routine queues a APC for execution when the right
 *     scheduler environment exists.
 *
 * @param Apc
 *        Pointer to an initialized control object of type APC for which the
 *        caller provides the storage.
 *
 * @param SystemArgument[1,2]
 *        Pointer to a set of two parameters that contain untyped data.
 *
 * @param PriorityBoost
 *        Priority Boost to apply to the Thread.
 *
 * @return If the APC is already inserted or APC queueing is disabled, FALSE.
 *         Otherwise, TRUE.
 *
 * @remarks The APC will execute at APC_LEVEL for the KernelRoutine registered,
 *          and at PASSIVE_LEVEL for the NormalRoutine registered.
 *
 *          Callers of this routine must be running at IRQL <= DISPATCH_LEVEL.
 *
 *--*/
// 判断能不能插入,然后调入内部函数
BOOLEAN
NTAPI
KeInsertQueueApc(IN PKAPC Apc,
                 IN PVOID SystemArgument1,
                 IN PVOID SystemArgument2,
                 IN KPRIORITY PriorityBoost)
{
    PKTHREAD Thread = Apc->Thread;
    KLOCK_QUEUE_HANDLE ApcLock; // APC锁
    BOOLEAN State = TRUE;
    ASSERT_APC(Apc);    // 测试APC是否有效
    ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);  // 断言IRQL

    /* Get the APC lock */
    // 获取APC 锁
    KiAcquireApcLock(Thread, &ApcLock); // ree

    /* Make sure we can Queue APCs and that this one isn't already inserted */
    // 判断APC是否可以插入,如果线程可插入标识为0,或者Apc插入标识为1,就设置返回值为FALSE
    if (!(Thread->ApcQueueable) || (Apc->Inserted))
    {
        /* Fail */
        // 插入不了
        State = FALSE;
    }
    else
    {
        // 可以插入
        /* Set the System Arguments and set it as inserted */
        // 获取两个参数给Apc,Apc插入状态设置为已插入
        Apc->SystemArgument1 = SystemArgument1;
        Apc->SystemArgument2 = SystemArgument2;
        Apc->Inserted = TRUE;

        /* Call the Internal Function */
        // 调用内部函数
        KiInsertQueueApc(Apc, PriorityBoost);
    }

    /* Release the APC lock and return success */
    // 释放APC锁,返回
    KiReleaseApcLockFromDpcLevel(&ApcLock); // ree
    KiExitDispatcher(ApcLock.OldIrql);
    return State;
}

KiInsertQueueApc

/*++
 * @name KiInsertQueueApc
 *
 *     The KiInsertQueueApc routine queues a APC for execution when the right
 *     scheduler environment exists.
 *
 * @param Apc
 *        Pointer to an initialized control object of type APC for which the
 *        caller provides the storage.
 *
 * @param PriorityBoost
 *        Priority Boost to apply to the Thread.
 *
 * @return None
 *
 * @remarks The APC will execute at APC_LEVEL for the KernelRoutine registered,
 *          and at PASSIVE_LEVEL for the NormalRoutine registered.
 *
 *          Callers of this routine must have locked the dipatcher database.
 *
 *--*/
VOID
    FASTCALL
    KiInsertQueueApc(IN PKAPC Apc,
                     IN KPRIORITY PriorityBoost)
{
    PKTHREAD Thread = Apc->Thread; // 获取线程
    PKAPC_STATE ApcState;
    KPROCESSOR_MODE ApcMode;
    PLIST_ENTRY ListHead, NextEntry;
    PKAPC QueuedApc;
    PKGATE Gate;
    NTSTATUS Status;
    BOOLEAN RequestInterrupt = FALSE;

    /*
     * Check if the caller wanted this APC to use the thread's environment at
     * insertion time.
     */
    // 判断Apc状态索引是不是等于InsertApcEnvironment,_KAPC_ENVIRONMENT枚举类型的最后一个
    if (Apc->ApcStateIndex == InsertApcEnvironment)
    {
        /* Copy it over */
        // 线程的APC索引设置给Apc的索引
        // 如果说Apc = insert,索引值(3)跟需求不一样,就需要从线程里把线程的索引拿来
        Apc->ApcStateIndex = Thread->ApcStateIndex;
    }

    /* Get the APC State for this Index, and the mode too */
    // 获取线程的APC状态
    ApcState = Thread->ApcStatePointer[(UCHAR)Apc->ApcStateIndex];
    // 获取Apc的Apc模式
    ApcMode = Apc->ApcMode;

    /* The APC must be "inserted" already */
    // 到这里APC必须是这个标志
    ASSERT(Apc->Inserted == TRUE);

    /* Three scenarios:
     * 1) Kernel APC with Normal Routine or User APC = Put it at the end of the List
     带有 NormalRoutine 回调的APC,属于正常APC,放到链表最后
     * 2) User APC which is PsExitSpecialApc = Put it at the front of the List
     如果用户APC是退出线程的APC,放在最前面,加急处理
     * 3) Kernel APC without Normal Routine = Put it at the end of the No-Normal Routine Kernel APC list
     没有NormalRoutine回调的内核APC,也是特殊加急APC,放在加急APC链表最后
     */

    // 这个if-else处理逻辑是进行插入操作的,普通APC插入链表尾部,特殊APC插入特殊APC链表尾部,退出线程APC插入链表头部
    // Apc 有无 NormalRoutine
    if (Apc->NormalRoutine) // 如果有 NormalRoutine,说明是非特殊APC
    {
        /* Normal APC; is it the Thread Termination APC? */
        // 判断是用户模式,且是退出线程APC,这是个特例
        if ((ApcMode != KernelMode) &&
            (Apc->KernelRoutine == PsExitSpecialApc))
        {
            /* Set User APC pending to true */
            // 设置状态
            Thread->ApcState.UserApcPending = TRUE;

            /* Insert it at the top of the list */
            // 头插链表
            InsertHeadList(&ApcState->ApcListHead[ApcMode],
                           &Apc->ApcListEntry);
        }
        else
        {
            /* Regular user or kernel Normal APC */
            // 普通APC,插入到链表最后
            InsertTailList(&ApcState->ApcListHead[ApcMode],
                           &Apc->ApcListEntry);
        }
    }
    else // 如果无 NormalRoutine,说明是特殊APC
    {
        /* Special APC, find the last one in the list */
        ListHead = &ApcState->ApcListHead[ApcMode]; // 链表首成员
        NextEntry = ListHead->Blink;                // 链表最后一个成员
        while (NextEntry != ListHead)
        {
            /* Get the APC */
            // 获取APC
            QueuedApc = CONTAINING_RECORD(NextEntry, KAPC, ApcListEntry);

            /* Is this a No-Normal APC? If so, break */
            // 没有这个回调函数就跳出,如果有就指向上一个
            // 找到加急链表最后一个
            // 正常APC有NormalRoutine这个,找到一个没的,就是加急APC的尾部
            if (!QueuedApc->NormalRoutine)
                break;

            /* Move to the previous APC in the Queue */
            // 链表从后往前遍历
            NextEntry = NextEntry->Blink;
        }

        /* Insert us here */
        InsertHeadList(NextEntry, &Apc->ApcListEntry);
    }

    // 检查Apc状态索引是否匹配
    /* Now check if the Apc State Indexes match */
    if (Thread->ApcStateIndex == Apc->ApcStateIndex)
    {
        /* Check that the thread matches */
        // 如果线程是当前线程
        if (Thread == KeGetCurrentThread())
        {
            /* Sanity check */
            // 并且线程在运行状态
            ASSERT(Thread->State == Running);

            /* Check if this is kernel mode */
            // 判断APC是不是内核模式
            if (ApcMode == KernelMode)
            {
                /* All valid, a Kernel APC is pending now */
                // 设置标志
                Thread->ApcState.KernelApcPending = TRUE;

                /* Check if Special APCs are disabled */
                // 检查是否禁用特殊APC
                if (!Thread->SpecialApcDisable)
                {
                    // 如果没有就中断
                    /* They're not, so request the interrupt */
                    // 则调用HalRequestSoftwareInterrupt请求软中断间接调用KiDeliverApc,来执行APC。
                    HalRequestSoftwareInterrupt(APC_LEVEL);
                }
            }
        }
        else //如果不是当前线程
        {
            // 获取程序调度锁
            /* Acquire the dispatcher lock */
            KiAcquireDispatcherLock();

            // 判断内核模式
            /* Check if this is a kernel-mode APC */
            if (ApcMode == KernelMode)
            {
                /* Kernel-mode APC, set us pending */
                Thread->ApcState.KernelApcPending = TRUE;

                /* Are we currently running? */
                if (Thread->State == Running)
                {
                    /* The thread is running, so remember to send a request */
                    // 请求中断标志设置为True
                    RequestInterrupt = TRUE;
                }
                // 等待状态
                else if ((Thread->State == Waiting) &&                // 线程为等待状态
                         (Thread->WaitIrql == PASSIVE_LEVEL) &&       // 等待IRQL = PASSIVE_LEVEL
                         !(Thread->SpecialApcDisable) &&              // 特殊APC禁用标识没有设置
                         (!(Apc->NormalRoutine) ||                    // 无NormalRoutine
                          (!(Thread->KernelApcDisable) &&             // 线程内核APC禁用标识没有设置
                           !(Thread->ApcState.KernelApcInProgress)))) // 线程APC状态内核APC执行标志没有设置
                {
                    /* We'll unwait with this status */
                    // 在这个状态下不等待
                    Status = STATUS_KERNEL_APC;

                    /* Wake up the thread */
                    // 唤醒线程
                    KiUnwaitThread(Thread, Status, PriorityBoost);
                }
                else if (Thread->State == GateWait) // 线程如果是门等待状态
                {
                    /* Lock the thread */
                    // 锁线程
                    KiAcquireThreadLock(Thread);
                    // 门等待状态
                    /* Essentially do the same check as above */
                    if ((Thread->State == GateWait) &&               // 线程为门等待状态
                        (Thread->WaitIrql == PASSIVE_LEVEL) &&       // 线程等待IRQL为PASSIVE_LEVEL
                        !(Thread->SpecialApcDisable) &&              // 线程特殊APC禁用标识没设置
                        (!(Apc->NormalRoutine) ||                    // APC没有NormalRoutine
                         (!(Thread->KernelApcDisable) &&             // 线程内核APC禁用标识没设置
                          !(Thread->ApcState.KernelApcInProgress)))) // 线程APC状态内核APC执行标识没设置
                    {
                        /* We were in a gate wait. Handle this. */
                        DPRINT1("A thread was in a gate wait\n");

                        // 设置门
                        /* Get the gate */
                        Gate = Thread->GateObject;
                        // 锁门
                        /* Lock the gate */
                        KiAcquireDispatcherObject(&Gate->Header);
                        // 删除当前结点
                        /* Remove it from the waiters list */
                        RemoveEntryList(&Thread->WaitBlock[0].WaitListEntry);
                        // 解锁
                        /* Unlock the gate */
                        KiReleaseDispatcherObject(&Gate->Header);
                        // 线程队列有效就自增
                        /* Increase the queue counter if needed */
                        if (Thread->Queue)
                            Thread->Queue->CurrentCount++;

                        // 设置线程等待状态
                        /* Put into deferred ready list with this status */
                        Thread->WaitStatus = STATUS_KERNEL_APC;
                        KiInsertDeferredReadyList(Thread);
                    }

                    /* Release the thread lock */
                    KiReleaseThreadLock(Thread);
                }
            }
            // 用户等待模式
            else if ((Thread->State == Waiting) &&        // 线程在等待状态
                     (Thread->WaitMode == UserMode) &&    // 线程等待模式为用户模式
                     ((Thread->Alertable) ||              // 线程状态为Alertable
                      (Thread->ApcState.UserApcPending))) // 或 线程APC状态为用户APC挂起
            {
                /* Set user-mode APC pending */
                // 设置用户APC挂起标记
                Thread->ApcState.UserApcPending = TRUE;
                // 状态设置为用户APC
                Status = STATUS_USER_APC;

                /* Wake up the thread */
                // 唤醒线程
                KiUnwaitThread(Thread, Status, PriorityBoost);
            }

            /* Release dispatcher lock */
            // 释放调度锁
            KiReleaseDispatcherLockFromDpcLevel();

            /* Check if an interrupt was requested */
            // 请求APC中断
            KiRequestApcInterrupt(RequestInterrupt, Thread->NextProcessor);
        }
    }
}

评论