- I've got a small question about the IRQL: APC_LEVEL.
- Posted by lostlander on November 5th, 2007
I think APC should be processed like following:
1, When an APC is issued, it's appended to the thread A's queue;
2, then the thread sheduler which runs at DISPATCH_LEVEL shedule
thread A to run, since A is in alertable state, so all of the APC
queue's function would be called.
3, when the APC function is being executed, the thread scheduler would
interrupt the APC execution since it runs at DISPATCH_LEVEL which is
higher than APC_LEVEL.
4, If the thread sheduler dertermines thread B should be the next
thread to run, then what happens now? Will thread B be blocked since
the previous IRQL is APC_LEVEL?
How does APC_LEVEL exactly interacts with thread scheduler which runs
at a higher priority????
Thanks for your reply.
- Posted by Maxim S. Shatskih on November 5th, 2007
APC_LEVEL does not suspend thread scheduling. Only >= DISPATCH_LEVEL does this.
As about APC execution - the APC has KernelRoutine which is called on APC_LEVEL
and NormalRoutine which is called on PASSIVE_LEVEL, and can be user mode.
If NormalRoutine is NULL - then this is a "special kernel APC", IIRC
IopCompleteRequest is the only "special kernel APC". These APCs are not blocked
by KeEnterCriticalRegion aka FsRtlEnterFileSystem. They are blocked only by
raising to APC_LEVEL.
As about other APCs - they are fire only when a) there is no critical region
AND b) IRQL is PASSIVE_LEVEL. The queued APCs are fired at the moment when both
conditions are switched to be TRUE.
For the kernel APC (NormalRoutine in kernel space) - KernelRoutine is called on
APC_LEVEL, then NormalRoutine is called on PASSIVE_LEVEL.
For the user APC (NormalRoutine in user space) - KernelRoutine is called on
APC_LEVEL, then NormalRoutine is "armed" for execution in user mode by patching
the trap frame on the kernel stack. The actual execution occurs on next return
from kernel to user mode, where the IRET opcode will return to
ntdll!KiUserApcDispatcher which later calls NormalRoutine.
Also, user and kernel APCs differ in their ability to interrupt waits. This is
documented on MSDN and depends on ProcessorMode and Alertable parameters for a
wait.
Also, there is RundownRoutine. It is called only if the thread is terminating
while still having some APCs queued, and its purpose is to properly free the
KAPC structure, which can be a part of a larger structure like IRP or so.
Ctrl-C is not an APC. It works by CreateRemoteThread in the target process,
this injected thread then calls the Ctrl-C handlers.
--
Maxim Shatskih, Windows DDK MVP
StorageCraft Corporation
maxim@storagecraft.com
http://www.storagecraft.com
- Posted by J de Boyne Pollard on November 5th, 2007
l> I think APC should be processed like following:
You're making the all-too-common mistake of thinking that IRQL is akin
to thread priority, i.e. that different threads have different IRQLs
and they somehow contend with one another and with the dispatcher. It
isn't.
First: The dispatcher isn't a separately executing entity that
interrupts threads. Interrupts interrupt threads. The dispatcher is
(to gloss over a few complexities) what is called to decide, at the
end of an interrupt service routine, whether to resume the interrupted
thread or switch to a different one.
Second: IRQL is akin to a CPU register. It's an abstraction of the
processor's actual interrupt prioritization hardware. To help
visualize this, consider the humble Motorola 68000 CPU. In that CPU,
interrupts as received by the processor itself have levels, and an
interrupt is only recognized if the level of interrupt that is
asserted on the processor's interrupt pins is greater than the current
interrupt level of the processor that is stored, encoded in 3 bits, in
the processor's Status Register.
The IRQL concept abstracts this idea, such that it is not specific to
any one processor architecture, and adds the idea of software
interrupts, as well as hardware interrupts, to the mix. The CPU, as
seen by you, the kernel-mode programmer, effectively has an IRQL
register, and all interrupts, software and hardware, have levels.
Interrupts are masked if they are less than or equal to the value in
the processor's IRQL register. Scheduling an APC or DPC is viewed as
asserting a software interrupt whose interrupt service routine is
invoked as soon as the CPU's IRQL register is set to a low enough
level for interrupts at that level to be unmasked.
There's a lot of mechanism under the covers to make this work, given
that real processors don't have the ability to mask their _actual_
software interrupt instructions. A TRAP instruction on a 680xx CPU or
an INT instruction on an x86 CPU is not maskable, so the whole
software interrupt mechanism has to be implemented in some other way.
(On Alpha processors, and on x86 processors with APICs, "software
interrupts" can actually be implemented in hardware, as if they were
real device interrupts. But, for x86 CPUs at least, this isn't done
with the processor's own software interrupt instructions.)
Nonetheless this is the abstraction that you are presented with.
- Posted by Peter Wieland [MSFT] on November 6th, 2007
If a thread is running at APC_LEVEL it will not block the dispatcher. The
dispatcher can swap the thread off (in which case the current IRQL is saved
in the thread's context) and run thread B at PASSIVE_LEVEL. After the
dispatcher next schedules thread A it will drop IRQL to the thread's
previous IRQL which will be APC_LEVEL again.
APC_LEVEL is distinct from PASSIVE_LEVEL to stop a new APC from interrupting
the current one.
-p
--
This posting is provided "AS IS" with no warranties, and confers no rights.
"lostlander" <lostlander.tom@gmail.com> wrote in message
news:1194267919.558493.228180@i13g2000prf.googlegr oups.com...