同步中断代码
以下因素使处理多处理器系统上的硬件中断的驱动程序代码复杂化:
每次设备中断时,它都会提供特定于中断的信息,这些信息是易失的,因为设备下次中断时可能会被覆盖。
设备中断相对较高的 IRQL 及其中断服务例程 (ISR) 可能会中断其他驱动程序代码的执行。
对于 DIRQL 中断,ISR 必须在持有驱动程序提供的旋转锁时在 DIRQL 中运行,以便 ISR 可以在保存易失性信息的同时防止其他中断。 DIRQL 可防止当前处理器中断,而旋转锁可防止另一个处理器中断。
ISR 必须快速运行,因为当 ISR 执行时设备无法中断。 ISR 执行时间过长可能会降低系统速度,或者可能导致数据丢失。
ISR 和延迟过程调用 (DPC) 例程通常必须访问 ISR 在其中存储设备的易失数据的存储区域。 这些例程必须彼此同步,以便它们不会同时访问存储区域。
由于所有这些因素,在编写处理中断的驱动程序代码时,必须使用以下规则:
只有 EvtInterruptIsr 回调函数访问可变中断数据,例如包含中断信息的设备寄存器。
EvtInterruptIsr 回调函数应将可变数据移动到驱动程序定义的中断数据缓冲区,驱动程序的 EvtInterruptDpc 回调函数、EvtInterruptWorkItem 回调函数或多个 EvtDpcFunc 回调函数可以访问该缓冲区。
如果驱动程序为其中断对象提供 EvtInterruptDpc 或 EvtInterruptWorkItem 回调函数,则存储中断数据的最佳位置是中断对象的 上下文空间。 中断对象的回调函数可以使用接收的对象句柄访问对象的上下文空间。
如果驱动程序为每个 EvtInterruptIsr 回调函数提供多个 EvtDpcFunc 回调函数,则可以在每个 DPC 对象的上下文空间中存储中断数据。
必须同步访问中断数据缓冲区的所有驱动程序代码,以便一次只能有一个例程访问数据。
对于 DIRQL 中断对象, EvtInterruptIsr 回调函数在持有中断对象的驱动程序提供的旋转锁时访问 IRQL = DIRQL 的此数据缓冲区。 因此,访问缓冲区的所有例程也必须在保持旋转锁时在 DIRQL 中运行。 (通常,中断的 EvtInterruptDpc 或 EvtDpcFunc 回调函数是唯一必须访问 buffer.)
访问中断数据缓冲区的所有例程( EvtInterruptIsr 回调函数除外)都必须执行以下操作之一:
- 调用 WdfInterruptSynchronize 以计划访问中断数据缓冲区的 EvtInterruptSynchronize 回调函数。
- 在调用 WdfInterruptAcquireLock 和 WdfInterruptReleaseLock 之间放置访问中断数据缓冲区的代码。
这两种技术都允许 EvtInterruptDpc 或 EvtDpcFunc 函数在保留中断的旋转锁的同时访问 DIRQL 上的中断数据。 DIRQL 可防止当前处理器中断,而旋转锁可防止另一个处理器中断。
如果设备支持多个中断向量或消息,并且要同步驱动程序对这些中断的处理,则可以将单个旋转锁分配给多个 DIRQL 中断对象。 框架确定中断集的最高 DIRQL,并且它始终在该 DIRQL 处获取旋转锁,以便同步代码不会被集中的任何中断向量或消息中断。
对于 被动级中断对象,框架在 IRQL = PASSIVE_LEVEL 调用驱动程序的 EvtInterruptIsr 回调函数之前获取被动级别中断锁。 因此,访问缓冲区的所有例程都必须获取中断锁或在内部同步缓冲区访问。 通常,中断的 EvtInterruptWorkItem 回调函数是唯一访问缓冲区的其他例程。 有关从 EvtInterruptWorkItem 回调函数获取中断锁的信息,请参阅该页的“备注”部分。
还可以通过将单个等待锁分配给多个被动级别中断对象来同步驱动程序对多个中断向量的处理。
如果某些处理 DIRQL 中断的代码必须在 IRQL = PASSIVE_LEVEL 运行,则 EvtInterruptDpc 或 EvtDpcFunc 回调函数可以创建一个或多个 工作项 ,以便代码将作为 EvtWorkItem 回调函数运行。
或者,在 KMDF 版本 1.11 及更高版本中,驱动程序可以通过调用 WdfInterruptQueueWorkItemForIsr 来请求中断工作项。 (回想一下,驱动程序的 EvtInterruptIsr 回调函数可以调用 WdfInterruptQueueWorkItemForIsr 或 WdfInterruptQueueDpcForIsr,但不能同时调用两者。)
如果请务必将驱动程序的 EvtInterruptDpc 和 EvtDpcFunc 回调函数彼此同步以及与设备关联的其他回调函数,则驱动程序可以在中断的WDF_INTERRUPT_CONFIG结构和 DPC 对象的WDF_DPC_CONFIG结构中将 AutomaticSerialization 成员设置为 TRUE。 或者,驱动程序可以使用 框架旋转锁。 (将 AutomaticSerialization 成员设置为 TRUE 不会将 EvtInterruptIsr 回调函数与其他回调函数同步。如本主题前面所述,使用 WdfInterruptSynchronize 或 WdfInterruptAcquireLock 同步 EvtInterruptIsr 回调函数。)
有关同步驱动程序例程的详细信息,请参阅 Framework-Based驱动程序的同步技术。