Programming 版 (精华区)

发信人: zpw (zhao), 信区: Programming
标  题: Synchronizing Multiple Interrupts
发信站: 紫 丁 香 (Wed Jul 29 21:06:42 1998), 转信

Synchronizing Multiple Interrupts
The Windows NT kernel provides a mechanism to protect data that is shared 
between an interrupt service routine (ISR) and code running at a lower 
priority level. If this mechanism were not available, then the ISR could 
execute while the lower priority code was in the process of modifying the 
shared data, potentially resulting in a catastrophic failure.

The mechanism that the system provides is the service KeSynchronizeExecution:

BOOLEAN KeSynchronizeExecution(
        IN PKINTERRUPT Interrupt,
        IN PKSYNCHRONIZEROUTINE SynchronizeRoutine,
        IN PVOID SynchronizeContext
        );
A driver calls KeSynchronizeExecution when it needs to access data shared with
the ISR. The parameter SynchronizeRoutine is the address of a function that 
actually accesses the shared data. The service calls the supplied function at 
raised IRQL, while holding the spin lock associated with the interrupt. If an 
interrupt occurs while SynchronizeRoutine is running, the execution of the ISR
is deferred until SynchronizeRoutine returns.

Now suppose the driver is managing not one, but possibly several interrupts. 
Each interrupt has its own priority level (IRQL). If the driver writer does 
not take precautions, then one ISR could interrupt the execution of a second 
ISR, raising the possibility of data corruption. 

One way to avoid the problem is to first determine which interrupt has the 
highest IRQL, and use KeSynchronizeExecution from inside the ISRs of interrupts
that have lower IRQLs. The ISR for the lower priority interrupt passes in a 
pointer to the interrupt object having the highest IRQL. The system takes the 
spin lock associated with the higher level interrupt, thereby ensuring atomic 
access to the critical data. This is just a generalization of the usage of 
KeSynchronizeExecution as described for sharing data between ISRs and 
non-interrupt code.

Because it could be expensive to make extra calls and to change IRQLs while in
an ISR, NT enables the driver writer to assign a common synchronization level 
for a set of interrupts. For a set of interrupts that have a common IRQL, the 
associated ISRs cannot interrupt one another, so they don't have to call 
KeSynchronizeExecution to ensure atomic access to data. The key to setting a 
common synchronization level for a set of interrupts is found in the service 
IoConnectInterrupt:

NTSTATUS IoConnectInterrupt(
        OUT PKINTERRUPT *InterruptObject,
        IN PKSERVICE_ROUTINE ServiceRoutine,
        IN PVOID ServiceContext,
        IN PKSPIN_LOCK SpinLock,
        IN ULONG Vector,
        IN KIRQL Irql,
        IN KIRQL SynchronizeIrql,
        IN KINTERRUPT_MODE InterruptMode,
        IN BOOLEAN ShareVector,
        IN KAFFINITY ProcessorEnableMask,
        IN BOOLEAN FloatingSave
        );
The parameter in question is SynchronizeIrql. To determine this value, the 
driver first collects the IRQLs for all of its interrupts (using 
HalGetInterruptVector), and selects the greatest. The driver must also 
initialize storage for a spin lock, and pass its address in parameter SpinLock.
The spin lock must remain in existence while the interrupts are hooked.

Once the driver has determined the greatest IRQL for all its interrupts, and 
has intialized a spin lock, it calls IoConnectInterrupt for each interrupt, 
using the same values for SynchronizeIrql and SpinLock for each call. A driver
that uses this technique causes all of its ISRs to run at the same IRQL, which
greatly simplifies the task of serializing access to critical data.

If you develop NT or WDM drivers with Vireo Driver::Works, it is very easy to 
use this method. The class that handles interrupts is called KInterrupt. There
are two forms of KInterrupt::Connect. The first form is the simple form, used 
when a driver has only one interrupt, or when multiple interrupts do not 
require synchronization:

        NTSTATUS KInterrupt::Connect(PKSERVICE_ROUTINE Isr, PVOID Context);

The second form is for the case when there are multiple interrupts requiring 
synchronization:

        NTSTATUS KInterrupt::Connect(
                PKSERVICE_ROUTINE Isr, 
                PVOID Context,
                PKSPIN_LOCK pSpin,
                KIRQL SynchIrql
                );
 

--
※ 来源:.紫 丁 香 bbs.hit.edu.cn.[FROM: yaoyu.hit.edu.cn]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:3.630毫秒