[转](74)分析 APC 插入过程 —— KeInsertQueueApc , KiInsertQueueApc

 

一、KeInsertQueueApc(调用 KiInsertQueueApc)

 

BOOLEAN
KeInsertQueueApc (
    IN PRKAPC Apc,
    IN PVOID SystemArgument1,
    IN PVOID SystemArgument2,
    IN KPRIORITY Increment
    )

/*++

Routine Description:

    This function inserts an APC object into the APC queue specifed by the
    thread and processor mode fields of the APC object. If the APC object
    is already in an APC queue or APC queuing is disabled, then no operation
    is performed. Otherwise the APC object is inserted in the specified queue
    and appropriate scheduling decisions are made.

	插入 APC 到指定线程的APC队列,用户态和内核态分别插入对应的 APC 队列。
	如果 APC 对象已经在 APC 队列或者 APC 队列被禁用(例如线程正在退出),
	则不执行操作。否则插入 APC 对象并调用相应的函数。

Arguments:

    Apc - Supplies a pointer to a control object of type APC.
	APC 结构

    SystemArgument1, SystemArgument2 - Supply a set of two arguments that
        contain untyped data provided by the executive.
	传给 APC 函数的参数。

    Increment - Supplies the priority increment that is to be applied if
        queuing the APC causes a thread wait to be satisfied.
	线程优先级增量

Return Value:

    If the APC object is already in an APC queue or APC queuing is disabled,
    then a value of FALSE is returned. Otherwise a value of TRUE is returned.

--*/

{

    BOOLEAN Inserted;
    KLOCK_QUEUE_HANDLE LockHandle;
    KIRQL OldIrql;
    PRKTHREAD Thread;

    ASSERT_APC(Apc);
    ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);

    //
    // Raise IRQL to SYNCH_LEVEL, acquire the thread APC queue lock, and lock
    // the dispatcher database.
    // 提升 IRQL 等级,申请 APC 锁,锁 dispatcher database

    Thread = Apc->Thread;
    KeAcquireInStackQueuedSpinLockRaiseToSynch(&Thread->ApcQueueLock, &LockHandle);
    KiLockDispatcherDatabaseAtSynchLevel();

    //
    // If APC queuing is disabled, then set inserted to FALSE. Else save
    // system parameter values in APC object, and attempt to queue APC.
    // 调用 KiInsertQueueApc

    if (Thread->ApcQueueable == FALSE) {
        Inserted = FALSE;

    } else {
        Apc->SystemArgument1 = SystemArgument1;
        Apc->SystemArgument2 = SystemArgument2;
        Inserted = KiInsertQueueApc(Apc, Increment);
    }

    //
    // Unlock the dispatcher database from SYNCH_LEVEL, unlock the thread APC
    // queue lock and lower IRQL to its previous value, and return whether the
    // APC was inserted.
    //

    KiUnlockDispatcherDatabaseFromSynchLevel();
    KeReleaseInStackQueuedSpinLock(&LockHandle);
    return Inserted;
}

 

二、KiInsertQueueApc

 

BOOLEAN
FASTCALL
KiInsertQueueApc (
    IN PKAPC Apc,
    IN KPRIORITY Increment
    )

/*++

Routine Description:

    This function inserts an APC object into a thread's APC queue. The address
    of the thread object, the APC queue, and the type of APC are all derived
    from the APC object. If the APC object is already in an APC queue, then
    no opertion is performed and a function value of FALSE is returned. Else
    the APC is inserted in the specified APC queue, its inserted state is set
    to TRUE, and a function value of TRUE is returned. The APC will actually
    be delivered when proper enabling conditions exist.

    N.B. The thread APC queue lock and the dispatcher database lock must both
         be held when this routine is called.

Arguments:

    Apc - Supplies a pointer to a control object of type APC.

    Increment - Supplies the priority increment that is to be applied if
        queuing the APC causes a thread wait to be satisfied.

Return Value:

    If the APC object is already in an APC queue, then a value of FALSE is
    returned. Else a value of TRUE is returned.

--*/

{

    KPROCESSOR_MODE ApcMode;
    PKAPC ApcEntry;
    PKAPC_STATE ApcState;
    BOOLEAN Inserted;
    PLIST_ENTRY ListEntry;
    PKTHREAD Thread;

    //
    // If the APC object is already in an APC queue, then set inserted to
    // FALSE. Else insert the APC object in the proper queue, set the APC
    // inserted state to TRUE, check to determine if the APC should be delivered
    // immediately, and set inserted to TRUE.
    //
    // For multiprocessor performance, the following code utilizes the fact
    // that kernel APC disable count is incremented before checking whether
    // the kernel APC queue is nonempty.
    //
    // See KeLeaveCriticalRegion().
    //

    Thread = Apc->Thread;
    if (Apc->Inserted) {
        Inserted = FALSE;

    } else {
		//typedef enum _KAPC_ENVIRONMENT {
		//	OriginalApcEnvironment, // 所属进程(创建线程的进程,父进程)
		//	AttachedApcEnvironment, // 挂靠进程
		//	CurrentApcEnvironment,	// 当前环境,提供CR3的进程(正常状态是所属进程,挂靠状态是挂靠进程)
		//	InsertApcEnvironment	// 插入APC时的环境
		//} KAPC_ENVIRONMENT;

		// Apc->ApcStateIndex 是在 KeInitializeApc 函数内初始化的,它的值决定了插入到哪个进程的APC队列(所属进程 还是 挂靠进程)
		// 
		// 终止线程的 PspTerminateThreadByPointer 和3环 QueueUserApc , Apc->ApcStateIndex 都是 0
		// 0意味着选择所属进程,因为不挂靠时 ApcStatePointer[0] 是所属线程;挂靠时 ApcStatePointer[0] 还是所属线程的备份

        if (Apc->ApcStateIndex == InsertApcEnvironment) {
			// 插入前实时地从 Thread 里取
			//设计 InsertApcEnvironment 也许是考虑到初始化时和插入前线程的状态可以发生改变

            Apc->ApcStateIndex = Thread->ApcStateIndex;
			
        }
		
        ApcState = Thread->ApcStatePointer[Apc->ApcStateIndex];

        //
        // Insert the APC after all other special APC entries selected by
        // the processor mode if the normal routine value is NULL. Else
        // insert the APC object at the tail of the APC queue selected by
        // the processor mode unless the APC mode is user and the address
        // of the special APC routine is exit thread, in which case insert
        // the APC at the front of the list and set user APC pending.
        //

		
        ApcMode = Apc->ApcMode; // 内核APC or 用户APC

		
        if (Apc->NormalRoutine != NULL) {
			// NormalRoutine 非空,就在这里插入
			// NormalRoutine 是 所有用户APC函数的入口 或者 内核APC函数,取决于APC是用户模式还是内核模式

            if ((ApcMode != KernelMode) && (Apc->KernelRoutine == PsExitSpecialApc)) {
				// 是用户APC,并且 KernelRoutine == PsExitSpecialApc ,3环调用 QueueUserApc 就是这种情况
                
				// 标记已插入
				Thread->ApcState.UserApcPending = TRUE;

				// 插入到队列头部
                InsertHeadList(&ApcState->ApcListHead[ApcMode],
                               &Apc->ApcListEntry);

            } else {
				// 内核APC,比较简单,直接插入队列尾部
                InsertTailList(&ApcState->ApcListHead[ApcMode],
                               &Apc->ApcListEntry);
            }

        } else {
			// NormalRoutine 是 NULL ,走这里

			// 从队尾开始遍历 APC 队列
			// 直到找到下一个 NormalRoutine 为空的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);
        }

		// 插入成功
        Apc->Inserted = TRUE;

        //
        // If the APC index from the APC object matches the APC Index of
        // the thread, then check to determine if the APC should interrupt
        // thread execution or sequence the thread out of a wait state.
        //

		// 条件成立,APC 和当前线程使用同一个进程
		// 要么没有attach,APC插入了所属进程
		// 要么attach了,APC插入的也是挂靠的进程
        if (Apc->ApcStateIndex == Thread->ApcStateIndex) {

            //
            // If the processor mode of the APC is kernel, then check if
            // the APC should either interrupt the thread or sequence the
            // thread out of a Waiting state. Else check if the APC should
            // sequence the thread out of an alertable Waiting state.
            //

            if (ApcMode == KernelMode) {
				// 标记已插入内核APC
                Thread->ApcState.KernelApcPending = TRUE;

                if (Thread->State == Running) {
					// 如果线程正在运行,则 APC 中断
                    KiRequestApcInterrupt(Thread->NextProcessor);

                } else if ((Thread->State == Waiting) &&		// 线程阻塞(等待)
                          (Thread->WaitIrql == 0) &&
                          ((Apc->NormalRoutine == NULL) ||
                          ((Thread->KernelApcDisable == 0) &&
                          (Thread->ApcState.KernelApcInProgress == FALSE)))) // 没有正在执行的 APC
				{

					// 修改线程状态为就绪,提升优先级
                    KiUnwaitThread(Thread, STATUS_KERNEL_APC, Increment, NULL);
                }

            } else if ((Thread->State == Waiting) &&							// 线程处于阻塞状态
                      (Thread->WaitMode == UserMode) &&							// 用户导致的阻塞
                      (Thread->Alertable || Thread->ApcState.UserApcPending))	// 是否可以被APC唤醒或者已经插入,SleepEx 可以设置 Alertable
			{

                Thread->ApcState.UserApcPending = TRUE;
				// 修改线程状态为就绪,提升优先级
                KiUnwaitThread(Thread, STATUS_USER_APC, Increment, NULL);
            }
        }

        Inserted = TRUE;
    }

    //
    // Return whether the APC object was inserted in an APC queue.
    //

    return Inserted;
}


---------------------
作者:hambaga
来源:CSDN
原文:https://blog.csdn.net/Kwansy/article/details/110285374
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By:CSDN,CNBLOG博客文章一键转载插件

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
kthread 中的 SchedulerApc 是用于在内核线程上调度 APC(Asynchronous Procedure Call)的方法。以下是一个简单的例子,演示如何使用 SchedulerApc: ``` void MyApcRoutine(PVOID Context, ULONG SystemArgument1, ULONG SystemArgument2) { // 在此处执行异步过程调用的代码 } void MyThreadRoutine(PVOID Context) { PKTHREAD pThread = (PKTHREAD)Context; // 在此处执行内核线程的代码 // 调度异步过程调用 KeInitializeApc(&pThread->Apc, pThread, OriginalApcEnvironment, MyApcRoutine, NULL, NULL, KernelMode, NULL); if (KeInsertQueueApc(&pThread->Apc, NULL, NULL, IO_NO_INCREMENT)) { // APC 已成功插入队列 } else { // APC 插入队列失败 } } int main() { // 创建内核线程 HANDLE hThread; OBJECT_ATTRIBUTES objAttrs = RTL_INIT_OBJECT_ATTRIBUTES(NULL, NULL); NTSTATUS status = PsCreateSystemThread(&hThread, THREAD_ALL_ACCESS, &objAttrs, NULL, NULL, MyThreadRoutine, NULL); if (!NT_SUCCESS(status)) { // 创建线程失败 return status; } // 等待线程退出 status = ZwWaitForSingleObject(hThread, FALSE, NULL); if (!NT_SUCCESS(status)) { // 等待线程失败 return status; } // 关闭线程句柄 ZwClose(hThread); return 0; } ``` 在上面的代码中,MyThreadRoutine 是内核线程的主函数,它执行一些操作后调用 KeInsertQueueApc 方法来将一个 Apc 对象插入到线程的 APC 队列中。然后,MyApcRoutine 会在内核线程上异步执行。注意:在实际代码中,需要根据具体的业务逻辑来编写 MyThreadRoutine 和 MyApcRoutine 函数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值