聪明的驱动程序编写人员知道 Windows 可以在任何时候抢占线程,而很有可能驱动程序正要在其中运行高优先级线程。并且在多处理器系统(包括带有超线程 CPU 的系统)上,驱动程序可以同时在多个处理器上并发运行。在任何一种情况下,驱动程序都必须同步对共享的可写数据的访问。但是您确切地知道哪些内核模式驱动程序例程可以被并发调用吗?从而确切地知道需要保护哪些数据吗?
可以同时运行的两个例程称为是并发 的。对于驱动程序而言,并发通常意味着操作系统可以在一个例程返回之前调用另一个例程。
当两个例程可以并发运行时,必须确保任何共享的可写数据在同一时间只能被一个例程访问(除非所有这些访问都是只读的)。如果您了解可以并发调用哪些例程,那么必要时可以在驱动程序中使用锁,在某些情况下,可以安排驱动程序的数据结构以避免不必要的锁。
哪些例程可以被并发调用取决于调用的对象。例如,两个被同一个驱动程序对象并发调用的驱动程序例程称为针对驱动程序对象并发。驱动程序例程可以针对驱动程序对象、设备对象或文件对象并发。
驱动程序对象并发。 大部分驱动程序例程针对驱动程序对象并发,因为大部分驱动程序任务都针对特定的文件对象或设备对象进行,而不是针对驱动程序对象本身进行。
设备对象并发。 改变物理设备状态的驱动程序例程(例如即插即用和电源管理调度例程)通常不会针对设备对象并发。
文件对象并发。处理传统 I/O 请求(例如创建、关闭、读取、写入等)的驱动程序例程通常操作文件对象,所以它们的并发性与文件对象的状态相关。例如,驱动程序的 DispatchCleanup 例程直到指向文件对象的句柄被关闭时才被调用。因此,在 DispatchClose 或 DispatchCreate 例程针对相同的文件对象运行时,不能针对给出的文件对象调用这个例程。
您可以通过检查“内核模式驱动程序的多处理器注意事项”一文中提供的并发表来确定可以并发调用哪些例程。这些表列出针对驱动程序、设备和文件对象并发的例程。
下面展示了来自文件对象并发表的一个摘录:
这个摘录显示 Cancel 例程针对自身以及文件对象调度例程的文件对象并发性。这个表提供下列关于何时可以针对特定的文件对象调用驱动程序的 Cancel 例程的信息:
• | 当 Cancel 例程运行时,可以针对同一个文件对象再次调用它。这意味着驱动程序必须能够支持同一时间为特定的文件对象同时取消多个 I/O 请求。 |
• | 当驱动程序的 DispatchCleanup 例程正在运行时(也就是说,当驱动程序在处理特定文件对象的 IRP_MJ_CLEANUP 请求时),可以为文件对象调用驱动程序的 Cancel 例程。 |
• | 当驱动程序的 DispatchClose 或 DispatchCreate 例程正在运行时(也就是说,当驱动程序在处理特定文件对象的 IRP_MJ_CLOSE 或 IRP_MJ_CREATE 请求时),不能为文件对象调用驱动程序的 Cancel 例程。(但是,已经计划在 Windows Vista 和 Windows Server 2008 上取消 IRP_MJ_CREATE 请求。) |
• | 当驱动程序的任何其他文件 I/O 调度例程正在运行时,可以为同一个文件对象调用 Cancel 例程。 |
您应该做什么?
• | 识别共享并可能被并发访问的可写数据和内存位置。使用锁来确保所有潜在的并发访问依次发生。 |
• | 尽可能隔离驱动程序特定的、设备特定的和文件对象特定的数据。 |
• | 假设您的驱动程序将在多处理器系统上运行,并进行相应的测试。 |