这与former question我的SleepEx
实际如何运作有些关联。 The docs表示只要APC排队,执行SleepEx
的线程就会恢复,并且当线程已经执行APC时,APC可能会继续排队。
那么线程持续执行APC需要多长时间?是否每当检查时队列为空,并且在那一刻SleepEx
将控制返回给调用者?或者只是该线程执行的一个APC,然后SleepEx
返回,另一次执行APC需要再次调用SleepEx
?
我感觉APC一直被执行,直到队列第一次为空。谢谢!
答案 0 :(得分:2)
执行APC直到队列为空。当您的代码从APC返回时 - 系统检查是另一个插入的APC - 如果是 - 下一个 APC 执行,直到线程中不再插入APC。在此SleepEx
之后(或任何其他可警告的等待API)将控制权交还给您
以下一种方式执行的APC - 当您调用某个api(SleepEx
,MsgWaitForMultipleObjectsEx
,WaitForSingleObjectEx
,WaitForMultipleObjectsEx
..并将函数的bAlertable参数设置为TRUE时内核检查是在线程对象中插入的APC。如果是 - 内核将用户模式线程上下文复制到它的堆栈,而不是将线程上下文(用户模式返回地址设置为ntdll.KiUserApcDispatcher
)并返回。结果代码不是从他进入内核的地方返回的(如果SleepEx
这是ZwDelayExecution
,它是内部调用的),而是返回KiUserApcDispatcher
。此api执行 APC ,然后调用ZwContinue
。这个api声明为:
NTSYSAPI NTSTATUS NTAPI ZwContinue(PCONTEXT Context, BOOLEAN TestAlert);
就位使用上下文保存在堆栈线程上下文中,返回到调用可警告api的点,TestAlert
指定是否应该检查是否插入了额外的apc。如果用ZwContinue
系统调用TestAlert == FALSE
将不再检查线程队列中的APC,直到你自己没有调用SleepEx
或其他特定的api - 那么将完全执行1个APC。但是在KiUserApcDispatcher
TestAlert
硬编码到TRUE
- 如果您了解汇编代码(KiUserApcDispatcher
很小),您可以自己查看这些内容