MSDN Kernel-Mode Driver Architecture学习笔记(4)——Kernel-Mode Driver Components (5)

29、集成电路测试技术解析 本文深入解析了集成电路测试技术,涵盖定制电路测试资源与策略、混合模拟/数字电路测试方法、数字测试模式生成难点与解决方案,以及可控性和可观测性的核心概念。同时详细讨论了固定故障、桥接故障和开路故障等主要故障模型及其检测方法。在测试技术的综合应用与优化方面,提出了合理配置测试资源、优化测试模式和设计测试流程的策略。最后展望了测试技术的发展趋势,包括集成化测试、自动化测试和智能化测试。通过全面掌握这些测试技术,可以有效提高集成电路的质量和可靠性,推动集成电路产业的发展。 阅读详情

随学随记,暂时未经编程验证 Written by HOOK_TTG(Jamie Jiang) 

 

9)        DispatchDeviceControl和DispatchInternalDeviceControl例程

一个驱动程序的DispatchDeviceControl和DispatchInternalDeviceControl例程分别处理IRP_MJ_DEVICE_CONTROL和IRP_MJ_INTERNAL_DEVICE_CONTROL I/O功能代码的IRPs。

对于每个普通类型的外围设备,系统为IRP_MJ_DEVICE_CONTROL请求定义了一套I/O控制代码。每个设备类型的新驱动程序必须指出这些请求。在大多情况下,这些用于每个设备类型的公共I/O控制代码是不对用户模式应用程序开放的。

这些系统定义的I/O控制代码中的一些由较高层驱动程序使用来创建用于底层设备驱动程序的IRPs,通过调用IoBuildDeviceIoControlRequest。其他一些被Win32组件使用来与底层设备驱动程序通讯,通过调用Win32函数DeviceIoControl轮流调用系统服务。I/O管理器建立一个IRP,并且保存主功能代码IRP_MJ_DEVICE_CONTROL和给定的I/O控制代码到IO栈存储单元结构,位于Parameters.DeviceIoControl.IoControlCode。随后,I/O管理器调用链路中顶层驱动程序的DispatchDeviceControl例程。

对于某些系统提供的驱动程序是为了交互操作而设计的并且支持新的驱动程序,操作系统也定义了一套用于IRP_MJ_INTERNAL_DEVICE_CONTROL请求的I/O控制代码。在大多数情况下,这些公共I/O控制代码允许附加的较高层驱动程序与一个底层设备驱动程序交互操作。

举个例子,系统提供的并行驱动程序支持一套内部的I/O控制代码,这些代码由厂家提供的驱动程序在IRP_MJ_INTERNAL_DEVICE_CONTROL中发送。

几乎所有通过系统定义I/O控制代码请求的操作都使用缓冲I/O,因为这个类型的请求很少需要传输很大数量的数据。换句话说,甚至建立用于直接I/O的设备对象的驱动程序也会被发送一些数据传入或者传出位于Irp->AssociatedIrp.SystemBuffer缓冲区的设备控制请求IRPs(除了某些类型的与Win32通讯驱动程序紧密连接的顶层设备驱动程序)。

另外,驱动程序可以定义一套私有I/O控制代码,其他驱动程序可以使用它们来通讯。新的公共I/O控制代码只有在与微软公司合作下才能被添加到系统,因为公共I/O控制代码是操作系统自身内建的。

1.         底层驱动程序中的DispatchDeviceControl

用于底层驱动程序的IRP_MJ_DEVICE_CONTROL请求要求驱动程序要么改变它的设备状态要么提供它的设备的状态信息。由于大多数类型的驱动程序需要处理许多I/O控制代码,因此它们的DispatchDeviceControl例程通常包含一个Switch语句,就像下面这样:

 

据上代码所示,DIspatchDeviceControl例程也会检查参数,有时是驱动程序必须支持的每个I/O控制代码,有时是一组这些I/O控制代码。

考虑如下实现指导方针,适用于设备驱动程序的DispatchDeviceControl例程:

l  DispatchDeviceControl必须检查检出的正确性,并且立即使用参数错误来完成IRPs。

l  在Case语句中分组I/O控制代码,就驱动程序性能和大小以及代码维护而言,在测试有效参数的时候都是很简练的。就像上面代码片段建议的那样,I/O控制代码要使用一个公共的结构,很自然的就选择了这样一个case分组。

l  第一个接入的I/O控制代码可以改善DispatchDeviceControl例程满足和完成IRP的性能,因为驱动程序可以很快的返回控制。

l  最后一个接入的I/O控制代码,说明是不常用的请求操作,也可以改善驱动程序处理IRP_MJ_DEVICE_CONTROL_REQUESTS的性能。

l  为了更好的性能,每个底层设备驱动程序的DispatchDeviceControl例程应该尽可能满足任何设备控制请求,除了将IRP排队到其他驱动程序例程。

如果DisptachDeviceControl例程可以完成这个IRP,那么它就应该调用IoCompleteRequest,PriortyBoost参数为IO_NO_INCREMENT。如果DispatchDeviceControl例程必须排队IRP以求进一步处理,那么它必须调用IoMarkIrpPending,并且返回STATUS_PENDING。

2.         高层驱动程序中的DispatchDeviceControl

通常,高层驱动程序的DispatchDeviceControl例程只是简单的为下一层驱动程序建立I/O栈存储单元,并传递IRP到IoCallDriver。DispatchDeviceControl例程很少检查输入IRP中参数的有效性,因为假定底层设备驱动程序有更好的有关如何处理每个设备类型指定的I/O控制请求的信息。

这个一般规则的一个可能例外就是类型/端口驱动程序对的类型驱动程序的DispatchDeviceControl。

任何新的不与一个特定设备驱动程序紧密相关的高层驱动程序,应该简单的为下一层驱动程序建立I/O栈存储单元,并且传递IRP_MJ_DEVICE_CONTROL请求以求更进一步处理。

一个设备的控制请求通常是同步处理的。也就是说,一个高层驱动程序的DispatchDeviceCOntrol例程可以时常按如下方式向系统返回控制:

 

然而,如果一个较低层驱动程序为这样一个请求返回STATUS_PENDING,那么一个高层驱动程序就不能使用前述的技术。在这种情况下,高层驱动程序应该调用IoSetCompletionRoutine来注册一个IoCompletion例程。在IoCompletion例程被调用的时候,它可以检查I/O状态块来确定IRP是否仍然处于pending(挂起)状态。如果是,那么IoCompletion例程可以重试这个请求,或者,可能,在它调用IoCompleteRequest之前调用IoMarkIrpPending,并返回STATUS_PENDING。高层驱动程序不必使用STATUS_PENDING来完成一个IRP,除非它首次为那个IRP调用IoMarkIrpPending。

如果底层设备驱动程序必须在它完成请求之前处理来自设备的大量数据传输,那么高层驱动程序可能异步处理这样一个设备控制请求。换句话说,高层驱动程序可能调用IoSetCompletionRoutine来注册一个IoCompletion例程,向底层驱动程序传递IRP,并且从它的DispatchDeviceControl例程返回控制。

几乎所有系统定义的I/O控制代码要求底层设备驱动程序传输只是适当数量的数据,通常少于PAGE_SIZE总额。一般来说,高层驱动程序应该同步处理这些请求,就像上面所示的代码段那样,因为底层驱动程序返回控制是非常快的。换句话说,调用高层驱动程序的IoCompletion例程的开销不能补偿任何额外的IRP处理,驱动程序可以在这样短的间隔完成。

使用IoBuildDeviceIoControlRequest为一个底层设备驱动程序分配IRPs的高层驱动程序可以异步处理这些设备控制请求。较高层驱动程序可以等待一个可选的事件对象,这个事件对象对传递到IoBuildDeviceIoControlRequest并且与驱动分配的IRP相关联。

3.         类型/端口驱动程序中的Dispatch(Internal)DeviceControl

一个类/端口对的较高层驱动程序有时在其DispatchDeviceControl例程中完成IRPs。例如,一个类驱动程序可能,在初始化过程中,搜集和存储有关底层设备的特征信息,在随后的IRP_MJ_DEVICE_CONTROL请求中将搜寻这些信息,从而经由不用传递到底层设备驱动程序的请求来达到节省处理时间。一个类驱动程序可能也被设计来检查IRP的参数和发送只有有效参数的请求到端口驱动程序。

紧密联系的类/端口驱动程序也可以定义一套驱动特定的内部I/O控制代码,类驱动程序可以用于到端口驱动程序的IRP_MJ_INTERNAL_DEVICE_CONTROL请求。

例如,在系统键盘和鼠标类驱动程序的DispatchCreateClose例程中发送系统定义的内部设备控制请求来使能或禁止键盘和鼠标中断到底层端口驱动程序。这些系统类驱动程序建立IRP_MJ_INTERNAL_DEVICE_CONTROL请求用于底层端口驱动程序。一些新的与这些系统类驱动程序交互操作的键盘或者鼠标端口驱动程序必须提供这些公共的内部设备控制请求。

系统并行类/端口驱动程序模型具有类似的特征。新的并行类驱动程序可以从系统并行端口驱动程序获取支持,通过建立用于带有公共IOCTL_PARALLEL_PORT_XXX控制代码的IRP_MJ_INTERNAL_DEVICE_CONTROL请求。可以替换系统并行端口驱动程序,但是任何一个新的驱动程序也必须提供这样一套公共的内部设备控制请求。

对于紧密联系的端口/类驱动程序,类驱动程序可能操作某些设备控制请求处理而不传递它们到端口驱动程序。在一个新的类/端口驱动程序对中,类驱动程序的DispatchDeviceControl例程可以下面任意一项:

l  检查在其拥有的I/O栈存储单元中参数的有效性,如果发现任何参数错误就设置I/O状态块,并调用IoCompleteRequest且设置参数PriortiyBoost值为IO_NO_INCREMENT;否则,调用IoGetNextIrpStackLocation复制其拥有的I/O栈存储单元到端口驱动程序的I/O栈存储单元中,并且与IoCallDriver一起传递IRP。

l  或者,什么都不做,只在IRP中建立端口驱动程序的I/O栈存储单元而不检查参数,并且传递其到端口驱动程序以求处理。

SCSI类驱动程序对于处理设备控制请求有特定的要求。

4.         编写Dispatch(Internal)DeviceControl的指导方针

l  在最小限度,一个较高层驱动程序必须从其拥有的在IRP中的I/O栈存储单元终复制用于IRP_MJ_DEVICE_CONTROL或者IRP_INTERNAL_DEVICE_CONTROL请求的参数到下一个较低层驱动程序的I/O栈存储单元。然后,还必须使用一个指向下一个较低层驱动程序的设备对象和IRP指针来调用IoCallDriver。

较高层驱动程序应该传送IoCallDriver返回的状态值,或者在其为一个较低驱动程序异步处理的请求返回控制时,在返回的IRP的I/O状态块中设置状态值。

l  底层设备驱动程序必须处理设备控制请求,除非其有一个紧密联系的类驱动程序,这个类驱动程序完成这些请求的一个子集。设备驱动程序的DispatchDeviceControl例程通常通过打开在每个IRP的I/O栈存储单元中的Parameters.DeviceIoControl.IoControlCode来开始处理这些请求。

l  较低层设备驱动程序应该检查传入请求的参数,并且如果有参数是无效的,那么就使用一个合适的错误来忽略这个IRP。大多数常见的检查这些请求参数的有效性有如下格式:

 

那里的状态值集是STATUS_BUFFER_TOO_SMALL或者STATUS_INVALID_PARAMETER之一。

l  每个设备驱动程序的DispatchDeviceControl或者DispatchInternalDeviceControl例程必须通过使用一个合适的NTSTATUS值设置I/O状态块来处理接收到一个未识别的I/O控制代码,设置其Information域为0,并且使用一个值为IO_NO_INCREMENT的PriorityBoost来完成这个IRP。

l  特定I/O控制代码的设备驱动程序处理必须包含任何设备类型特定的、用于相同类型设备的系统定义的I/O控制代码。查看WDK中的设备特定章节中更多信息,关于对于每个设备类型和相应头文件的系统需求,每个都是ntdd前缀开始,用于这些I/O控制代码的系统定义的结构声明。

l  紧密联系的类/端口驱动程序对的类驱动程序可以处理和完成一个设备控制请求子集,而不传递它们到底层端口驱动程序。然而,这样一个类驱动程序必须传递所有有效设备控制请求,要求一个用于设备的状态改变并且要求返回有关设备的不确定信息,比如当前的波特率,卷,或者视频模式。

10)        DIspatchPnp例程

驱动程序的DispatchPnp例程通过处理IRP_MJ_PNP I/O功能代码来支持即插即用。与IRP_MJ_PNP功能代码相关联的是许多次要的I/O功能代码,这些功能代码中的一些是所有驱动程序必须处理的,一些是可选择处理的。PnP管理器使用这些次要功能代码来指挥驱动程序启动、停止和移除设备以及查询关于他们设备的驱动程序。

设备的所有驱动程序必须有条件去处理用于设备的PnP IRPs,除非在功能或者过滤驱动程序被允许忽略这个IRP此类少数情况。

每个驱动程序的DispatchPnP例程必须依据这些规则:

l  功能或者过滤驱动程序必须向下传递PnP IRPs到设备栈中的下一个驱动程序,除非功能或者过滤驱动程序处理这个IRP并且遇到一个错误(比如资源不足)。

所有用于设备的驱动程序必须有条件处理PnP IRPs,除非这些驱动程序中的某一个遇到一个错误。PnP管理器发送IRPs到设备栈中的顶部驱动程序。功能和过滤驱动程序向下传递IRP到下一个驱动程序,并且父总线驱动程序完成这个IRP.

驱动程序可以忽略一个IRP,如果它尝试处理IRP并且遇到一个错误(比如资源不足)。如果驱动程序接收到一个带有其不能处理的代码的IRP,驱动程序则不能忽略这个IRP。并且它必须对IRP的状态不做修改的向下传递这样一个IRP到下一个驱动程序。

l  驱动程序必须处理某些PnP IRPs并且可以选择处理其他IRPs。

每个PnP驱动程序都要求处理某些IRPs,比如IRP_MN_REMOVE_DEVICE,并且可以选择地处理其他IRPs。

驱动程序可以使用一个合适的错误状态来忽略一个必须的PnP IRP,但是驱动程序不能为这样一个IRP返回一个STATUS_NOT_SUPORTED。

l  如果驱动程序处理成功的处理了一个PnP IRP,驱动程序设置IRP状态为成功。它不依赖于在栈中的另一个驱动程序来设置这个状态。

驱动程序设置Irp->IoStatus.Status为STATUS_SUCCESS来通知PnP管理器这个驱动程序处理IRP成功了。对于有些IRPs,一个非总线驱动程序可能会依赖其父总线驱动程序来设置状态值为成功。然而,这是一个危险的实践。出于一致性和健壮性考虑,驱动程序必须为每个它成功处理的PnP IRP设置IRP 状态为成功。

l  如果驱动程序忽略了一个IRP,驱动程序使用一个错误状态值来完成这个IRP,并且不再向下一个驱动程序传递这个IRP。

为了忽略一个类似IRP_MN_QUERY_STOP的IRP,驱动程序可以设置Irp-〉IoStatus.Status为STATUS_SUCCESSFUL。额外的错误状态值用于其它IRPs,包括STATUS_INSUFFICIENT_RESOURCES和STATUS_INVALID_DEVICE_STATE。

驱动程序不为他们处理的IRP设置STATUS_NOT_SUPPORTED。这个是由PnP管理器设置的最初状态。如果一个IRP被以这个状态完成,那就意味着栈中没有驱动程序处理这个IRP;所有驱动程序直接传递这个IRP到下一个驱动程序。

l  驱动程序必须在其派遣例程(在IRP的路径向下到设备栈)中处理一个PnP IRP,在一个IoCompletion例程(在IRP的路径倒退的设备栈),或者两者都有,就像IRP参考页面指定的那样。

有些PnP IRPs,比如IRP_MN_REMOVE_DEVICE,必须首先被在设备栈顶部的驱动程序处理,然后才是每个下一层驱动程序。其他的,比如IRP_MN_START_DEVICE,必须首先被父总线驱动程序处理。还有其他,比如IRP_MN_QUERY_CAPABILITIES,可以在设备栈一路向下处理也可以反向处理。

l  驱动程序必须添加信息到一个IRP在IRP的路径往下到设备栈和修改或者移除信息在IRP的路径反向。

在回应一个PnP查询IRP返回信息时,驱动程序必须按照这个规则来使得有序的信息被设备的分层驱动程序传递。

l  除非文档明确指出,否则驱动程序不必信赖PnP IRPs的发送次序。

l  在驱动程序发送一个PnP IRP时,它必须发送IRP到设备栈的顶层驱动程序。

大多数PnP IRPs由PnP管理器发送,但是有些可以被驱动程序发送(例如,IRP_MN_QUERY_INTERFACE)。驱动程序必须发送发送一个PnP IRP到设备栈顶层的驱动程序。调用IoGetAttachedDeviceRefrence来为设备栈顶部的驱动程序获取一个指向设备对象的指针。

应该使用一个操作系统的(Checked build)检查构建来测试驱动程序。系统的检查构建检验驱动程序是否符合上面所列的许多PnP规则。

11)        DispatchPower例程

驱动程序的DispatchPower例程通过处理用于IRP_MJ_POWER I/O功能代码的IRPs来支持电源管理。与IRP_MJ_POWER功能代码相关联的是许多用于电源管理的次要I/O功能代码。电源管理器使用这些次要的功能代码来指挥驱动程序来改变电源状态,等待和响应系唤醒事件,以及查询关于它们设备的驱动程序。

每个驱动程序的DispatchPower例程执行下面的任务:

l  处理IRP,如果可能。

l  传递IRP到设备栈中的下一个较低层驱动程序,使用PoCallDriver。

l  如果是一个总线驱动程序,则在设备上执行请求的电源操作,并且完成这个IRP。

所有用于设备的驱动程序必须有条件处理用于设备的电源IRPs,除非在很少情况,功能或者过滤驱动程序被允许忽略这个IRP。大多数功能和过滤驱动程序要么执行一些处理要么为每个电源IRP设置一个IoCompletion例程,然后不完成这个IRP而向下传递到下一个较低层驱动程序。

12)        DIspatchQueryInformation例程

例程用来处理IRP_MJ_QUERY_INFORMATION I/O功能代码的IRPs。驱动程序对这个I/O代码的支持是可选的,而且通常出现在高层或者文件系统驱动程序中。这个请求由I/O管理器和其他操作系统组件,还有其他内核模式驱动程序发送。例如,当一个用户模式应用程序调用GetFileInformationByHandle的时候,还有当一个内核模式组件调用ZqQueryInformationFile的时候将会发送这个请求。最后IRP到达总线驱动程序,如果需要则物理改变设备的电源状态,并且完成这个IRP。

当IRP被完成,I/O管理器调用任一IoCompletion例程。驱动程序是否需要设置一个完成例程取决于IRP类型和驱动程序的个别需求。

给设备上电的电源IRPs必须被设备栈中最底层的驱动程序首先处理(底层总线驱动程序),然后由栈中依次往上的驱动程序处理。给设备断电的电源IRPs必须被设备栈中顶层的驱动程序首先处理,然后由栈中依次往下的驱动程序处理。

用于可移动设备的特殊处理

在它们的DispatchPower例程中,可移动设备的驱动程序应该检查看看设备是否还在。如果设备已经移除了,驱动程序就不用向下传递这个IRP到下一个较低层驱动程序。反而,驱动程序应该做下面操作:

l  调用PsStartNextPowerIrp来开始处理下一个电源IRP。

l  设置Irp->IoStatus.Status为STATUS_DELETE_PENDING。

l  调用IoCompleteRequest,指定IO_NO_INCREMENT,来完成这个IRP。

l  返回STATUS_DELETE_PENDING。

13)        DispatchSetInformation例程

此例程用来处理IRP_MJ_SET_INFORMATION I/O功能代码的IRPs。驱动程序对这个I/O功能代码的支持是可选的,而且通常出现在高层或者文件系统驱动程序中。这个请求由I/O管理器和其他操作系统组件发送,还有其他内核模式驱动程序。例如,当一个用户模式应用程序调用SetEndOfFile的时候,还有一个内核模式组件调用ZwSetInformationFile的时候将会发送这个请求。

14)        DispatchFlushBuffers例程

此例程用来处理IRP_MJ_FLUSH_BUFFERS I/O功能代码的IRPs。驱动程序对这个I/O功能代码的支持是可选的,但是维持内核数据缓冲区的所有文件系统或者过滤驱动程序都必须处理这个请求,是为了在系统关闭前能将文件数据或者元数据的改变保存下了。这个请求由I/O管理器发送或者其他操作系统组件,还有其他内核模式驱动程序,在需要将缓冲的数据转存至磁盘的时候。例如,一个用户模式应用程序调用FlushFileBuffers的时候就会发送这个请求。

15)        DispatchShutdown例程

此例程用来处理IRP_MJ_SHUTDOWN I/O功能代码的IRPs。具有内部数据高速缓冲区的大容量存储设备的驱动程序必须处理这个请求。如果底层驱动程序维护内部数据缓冲区,那么大容量存储设备的驱动程序和层叠其上的中间层驱动程序也必须处理这个请求。

16)        DispatchSystemControl例程

此例程用来处理IRP_MJ_SYSTEMCONTROL I/O功能代码的IRPs。

所有的驱动程序必须提供DispatchSystemControl例程。这个例程的作用是提供对Windows管理规范(WMI)的支持。一个驱动程序不管是否支持WMI,这个例程都必须将这个IRP向下传递到下一层驱动程序。

8、            编写Unload例程

在系统正在运行的时候,任何驱动程序要能被替换或者卸载和重新载入,必须有一个Unload例程。所有WDM驱动程序都必须有Unload例程。

尽管Unload例程对非WDM驱动程序是可选的,但是驱动程序验证器将忽略任何没有提供Unload例程的驱动程序。

1)         Unload例程的情形

在驱动程序将被替换时或者驱动程序服务的所有的设备都被移除时,操作系统卸载一个驱动程序。如果驱动程序在其处理完一个IRP_MN_REMOVE_DEVICE请求之后再没有设备对象,那么PnP管理器将调用一个PnP驱动程序的Unload例程。

在卸载顺序的开始,I/O管理器或者PnP管理器标记驱动程序对象和它的设备对象为“Unload Pending(待卸载)”。在一个驱动程序被标记为“待卸载”之后,另外的驱动程序将无法再关联到那个驱动程序上,也不能创建驱动程序的设备对象的任何额外引用。驱动程序可以完成为解决的IRPs,但是系统将不会发送任何新的IRPs到这个驱动程序。

在下列所有情况都为真时,I/O管理器将调用一个驱动程序的Unload例程:

l  再也没有对驱动程序创建的设备对象的引用。换句话说,没有底层设备相关的文件能被打开,也没有用于任何驱动程序的设备对象的IRPs未解决。

l  没有其他驱动程序继续附加在这个驱动程序上。

l  驱动程序调用了IoUnregisterPlugPlayNotification来撤销注册先前注册的所有PnP通知。

要注意的是,如果驱动程序的DriverEntry例程返回一个错误状态,那么Unload例程将不会被调用。在这种情况下,I/O管理器简单的释放驱动程序占用的内存空间即可。

在系统关机期间,PnP管理器和I/O管理器都不会调用Unload例程。驱动程序要必须执行关闭处理的话应该注册一个DispatchShutdown例程。

2)         Unload例程的功能

驱动程序的Unload例程的职能取决于驱动程序是否支持PnP。

正像PnP驱动程序的DriverEntry例程通常很简单那样,它们的Unload例程也一样,在MSDN的A PnP Driver’s Unload Routine章节中描述。

一个非PnP驱动程序的Unload例程必须释放设备对象和驱动程序分配的资源。简单来说,它必须撤销其相应的DriverEntry和Reinitialize例程在初始化驱动程序时所作的工作,它的设备,它的资源。参看MSDN中A Non-PnP Driver’s Unload Routine章节。

a)         PnP驱动程序的Unload例程

PnP驱动程序必须有一个Unload例程,用来移除任何驱动程序特定的资源,比如内存、线程和事件,这些都是在DriverEntry例程中创建的。

驱动程序的Unload例程可以在所有驱动程序的设备被移除之后的任何时候被调用。PnP管理器调用一个驱动程序的Unload例程,使用一个IRQL=PASSIVE_LEVEL的系统线程上下文。

PnP驱动程序在响应PnP设备移除IRPs时释放设备特定的资源和设备对象。PnP管理器发送这些IRPs,代表它枚举到的每个PnP设备,还使用IoReportDetectedDevice得到任何根枚举的传统设备的一个驱动程序报告。

所以,PnP驱动程序的Unload例程通常很简单的,经常仅仅有一个Return语句组成。然而,如果驱动程序在其DriverEntry例程中分配任何驱动程序范围的资源,它必须在其Unload例程中释放那些资源除非它已经做过了。一般来说,释放一个PnP驱动程序的处理是一个同步操作。

I/O管理器释放驱动程序对象和任何驱动程序对象扩展,驱动程序使用IoAllocateDriverObjectExtension分配的。

b)        非PnP启动程序的Unload例程

早期的驱动程序和高级文件系统驱动程序,它们不处理PnP设备移除请求,所以必须在其Unload例程中释放资源,删除设备对象,和从设备栈分离出来。

如果早先还没那么做,那么一个传统设备驱动程序在其Unload例程中要做的第一件事是禁止来自设备的中断。否则,在Unload例程正在释放在设备扩展中ISR需要处理这个中断的资源时,它的ISR可能会被调用来处理一个设备中断。甚至如果在这些情况下它的ISR成功的运行 ,ISR队列的DpcForIsr或者CustomDpc例程,还有可能运行在IRQL>=DISPATCH_LEVEL下的其他驱动程序例程,将在Unload例程重新获得控制之前执行,因此增加了Unload例程删除另一个驱动程序例程引用的资源的可能性。

在禁止中断之后,文件系统和传统驱动程序必须释放资源和对象。

释放驱动程序分配的资源

驱动程序如何使用注册表的细节,在其设备扩展、控制器扩展或者驱动程序分配的非分页池中建立系统对象和资源,不同于驱动程序到驱动程序。然而,任何Unload例程必须释放驱动程序在场景中使用的资源。

任何驱动程序的Unload例程必须确保在它释放资源之前没有其他驱动程序例程当前在使用或者即将要使用的特别的资源。

一般来说,Unload例程在以下阶段释放所有驱动程序分配的资源:

1、  如果驱动程序还没有这样做,那么就禁止任何物理设备上的中断,如果可能,然后一旦中断被禁止就调用IoDisconnectInterrupt。

2、  确保没有其他驱动程序例程能引用Unload例程准备释放的资源。

例如,如果驱动程序用于一个特别的设备对象的IoTimer例程当前是使能的状态,那么Unload例程必须调用IoStopTimer。还必须确保没有线程在等待任何驱动程序的派遣对象,还有它的定时器对象在它释放用于其派遣对象的存储前没有被加入调用它的CustomTimerDpc例程的队列中。如果它有一个ISR可能已经加入队列的customDpc例程,那么它必须调用KeRemoveQueueDpc,等等。

如果驱动程序调用IoQueueWorkItem,那么它必须确保其作业项目已经被完成。IoQueueWorkItem获取一个关联设备对象的引用;如果任何这样的引用还存在,那么驱动程序就不能被卸载。

如果驱动程序调用了PsCreateSystemThread,那么Unload例程也必须处使驱动程序创建的线程运行起来,这样以来线程它自己可以在驱动程序被卸载之前调用PsTerminateSystemThread。驱动程序不能释放驱动程序创建的系统线程,要通过使用PsCreateSystemThread返回的Thread Handle调用ZwClose。

3、  释放任何驱动程序分配的设备特定的资源。这样做要涉及到调用下列系统支持例程:

l  IoDeleteSymbolicLink:如果DriverEntry或者Reinitialize例程调用过IoCreateSymbolicLink或者IoCreateUnprotectedSymbolicLink,IoDeassignArcName:如果驱动程序调用过IoAssignArcName。

l  ExFreePool:如果DriverEntry或者任何其他驱动程序例程调用过ExAllocatePoolWithTag并且驱动程序还没有释放分配的内存。

l  MmUnmapIoSpace:如果DriverEntry或者Reinitialize例程调用过MmMapIoSpace。

l  MmFreeNonCachedMemory:如果DriverEntry或者Reinitialize例程调用过MmAllocateCachedMemory。

l  MmFreeContiguousMemory:如果DriverEntry或者Reinitialize例程调用过MmAllocateContiguousMemory。

l  FreeCommonBuffer:如果DriverEntry或者Reinitialize例程调用过AllocateCommonBuffer。

l  IoAssignResources或者IoReportResourceUsage:如果DriverEntry或者Reinitialize例程调用过这些支持例程中的一个或者HalAssignSlotResources来为它自己和/或者它的独立物理设备获取在配置注册表中的硬件资源。

4、  释放系统对象和资源,就是那些DriverEntry或者Reinitialize例程建立在设备对象的设备扩展中的或者在控制器对象(如果创建了)的控制器扩展中的资源。尤其是,驱动程序必须在它尝试删除设备对象(IoDeleteDevice)或者控制器对象(IoDeleteController)之前做下面操作:

l  调用IoDisconnectInterrupt来释放到存在相应设备或者控制对象扩展中的中断对象指针。

l  使用指向下一个较低层的驱动程序的文件对象的指针来调用ObDerefrenceObject,如果它调用过IoAttachDevice或者IoAttachDeviceToDeviceStack并且将这个指针存储在一个设备或者控制器扩展中。

l  使用指向较低层驱动程序的设备对象来调用IoDetachDevice,如果它调用过IoAttachDevice或者IoAttachDeviceToDeviceStack并且将这个指针存储在一个设备或者控制器扩展中。

5、  释放硬件资源,就是那些DriverEntry或者Reinitialize例程获取的用于驱动程序的物理设备资源,如果有的话,位于注册表中/Registry/Machine/Hardware/ResourceMap树下面。

6、  移除任何用于其设备的名字,同样,DriverEntry或者Reinitialize例程存储在注册表中/Registry../DeviceMap树下面。

在驱动程序释放设备、系统和硬件资源之后,它就可以删除它的设备和控制器对象可,就像下面章节中描述的那样。

释放设备和控制器对象

在驱动程序删除一个设备或者控制对象之前,它必须先释放它对外部资源的引用,比如其他驱动程序对象指针和/或者中断对象指针,这些资源被存储在相应的设备或者控制器扩展中。然后可以为驱动程序创建的每个设备对象调用IoDeleteDevice。一个先前调用过IoCreateController的非WDM驱动程序必须还要调用IoDeleteController。

任何内核定义的对象,驱动程序为其在设备扩展中提供存储空间,是在Unload例程使用相应的设备对象调用IoDeleteDevice时被自动释放的。一般来说,任何由DriverEntry或者Reinitialize例程通过调用KeInitializeXxx建立的对象可以通过调用IoDeleteDevice而被释放,如果驱动程序为那个对象在其设备扩展中提供存储空间的话。例如,如果驱动程序有一个CustomTimerDpc例程,而且为必需的DPC和定时器对象在其设备扩展中提供存储空间,那么调用IoDeleteDevice来释放这些系统资源。

同样地,任何内核定义的对象,驱动程序为其在一个控制器扩展中提供存储空间,是在Unload例程使用相应的控制器对象调用IoDeleteController时被自动释放的。

如果DriverEntry或者Reinitialize例程曾调用IoGetConfigurationInformation来增加一个特定类型设备的计数,那么Unload例程还必须调用IoGetConfigurationInformation并且减少在I/O管理器的全局配置信息结构中其设备的计数,就像它删除相应的设备对象那样。

在它返回控制之前,Unload例程还负责释放任何其他驱动程序分配的资源,就是那些还没有被其他驱动程序例程释放的资源。

 

60、磁悬浮主动隔振系统的LQR控制研究 本研究针对磁悬浮主动隔振系统,提出了一种结合ANN建模、LQR控制与MPGA优化的综合控制方案。通过建立系统的动力学与状态空间模型,利用混合训练算法的ANN精确描述MSVI的非线性和滞后特性,并设计基于最小力传递率的LQR控制器。为优化LQR的Q和R矩阵,引入多群体遗传算法(MPGA),有效克服传统GA的早熟收敛问题,提升搜索效率与控制性能。仿真结果表明,在单频、扫频、随机及冲击激励下,该方法显著降低传递力幅值,隔振效果远优于被动系统。研究验证了所提方法的有效性,为磁悬浮隔振技术的实际应用奠定了基础,并展望 阅读详情

相关推荐

Windows下用Anaconda安装d2l库避坑指南(附PermissionError解决方案)

本文详细介绍了在Windows系统下使用Anaconda安装d2l库的完整指南,特别针对常见的PermissionError问题提供了多种解决方案。从环境准备到库安装,再到VSCode配置和数据路径处理,帮助读者顺利搭建深度学习环境并运行《动手学深度学习》的代码示例。

3a9bq4r8t2y的博客 225

管理PnP状态转换

<!--google_ad_client = "pub-2050647664789618";//728x90, 创建于 07-11-28google_ad_slot = "5411034739";google_ad_width = 728;google_ad_height = 90;//--><script type="text/javascript"src="http

倪勋的专栏 6005

格拉姆角场(GAF)实战:用Python实现时间序列图像化处理

本文深入解析了格拉姆角场(GAF)技术,这是一种将一维时间序列转化为二维图像特征的方法,以便利用卷积神经网络(CNN)等图像模型进行深度分析。文章通过Python实战代码,详细演示了GASF和GADF两种核心转换的实现过程,并探讨了其在时序数据分类任务中的应用,为故障预警、金融模式识别等场景提供了强大的图像驱动分析视角。

weixin_29079671的博客 273

【Windows内核】关于驱动例程的中断等级:PASSIVE、APC、DISPATCH、DEVICE的详细说明【2023.04.27】

结合生活来理解一下中断。我叫杨奶粉,是一名社畜,当我正在干活的时候,小组长让我去打印一份资料,我就放下手头的工作去打印资料(任务被中断)。这时候部门经理又让我去接待下客户,于是我就放下手头的打印,去接待客户(低优先级中断被高优先级中断打断)。接待客户的时候,刘副总和邓副总(同级别)又叫我参加不同的会议(被多个同级中断打断任务)。上面的例子和操作系统中的中断大差不差。死锁:当我在参加刘副总的会议的时候,需要打印个东西,那我就去打印东西,但是一走到打印机前面,就想起来自己正在参加副总的会,怎么能去打印呢?

qq_25231249的博客 2444

瑞芯微RK3576上手实测:8nm新U配6TOPS NPU,做AIoT项目选它还是RK3588/RK3568?

本文深度评测瑞芯微RK3576芯片在中高端AIoT项目中的表现,对比RK3588和RK3568的性能差异。RK3576采用8nm工艺,配备6TOPS NPU,在解码能力、AI推理效率和功耗控制方面表现均衡,是中端项目的黄金选择。实测数据显示其在多路视频解码和AI加速性能上优势明显,特别适合智能摄像头和工业HMI设备。

weixin_42627853的博客 716

开关电源拓扑结构选型指南:从Buck到Full-Bridge的实战解析

本文为硬件工程师提供了开关电源拓扑结构的实战选型指南。文章系统解析了从基础的Buck、Boost、Buck-Boost,到隔离型的Flyback、Forward,再到中高功率的半桥、全桥以及高效率的LLC谐振拓扑的适用场景与核心特点。通过对比分析各拓扑的优缺点,并结合输入输出电压、功率等级、成本与效率等关键参数,给出了清晰的选型决策路径,帮助工程师根据具体项目需求选择最合适的电源“骨架”。

weixin_29306261的博客 106

IO_REMOVE_LOCK使用方法小结

IO_REMOVE_LOCK(删除锁)的具体结构没有公开,WDK的文档中中查不到IO_REMOVE_LOCK。最开始看到IO_REMOVE_LOCK是在WDK的例子event中。下面是参考网上的一些资料之后的一点总结,错误的地方请指正。 为什么要用IO_REMOVE_LOCK? WDM 驱动程序在处理设备删除 IRP 并释放驱动程序分配的内存后可能接收到附加的 IRP。在处理附加的 I

msbsod的专栏 586

YOLOv8改进-bifpn

跨尺度连接通过添加一个跳跃连接和双向路径来实现,自此实现了加权融合和双向跨尺度连接。该结构是加权且双向连接的,即自顶向下和自底向上结构,通过构造双向通道实现跨尺度连接,将特征提取网络中的特征直接与自下而上路径中的相对大小特征融合,保留了更浅的语义信息,而不会丢失太多的深层语义信息。传统的特征融合时将尺度不同的特征图以相同权重进行加权,但是当输入的特征图分辨率不同时,以相同的权重进行加权对输出的特征图不平等。所以BiFPN根据不同输入特征的重要性设置不同的权重,同时反复采用这种结构来加强特征融合。

weixin_45303602的博客 8139

IO_REMOVE_LOCK(删除锁)

IO_REMOVE_LOCK(删除锁)的具体结构没有公开,WDK的文档中中查不到IO_REMOVE_LOCK。最开始看到IO_REMOVE_LOCK是在WDK的例子event中。下面是参考网上的一些资料之后的一点总结,错误的地方请指正。 为什么要用IO_REMOVE_LOCK? WDM 驱动程序在处理设备删除 IRP 并释放驱动程序分配的内存后可能接收到附加的 IRP。在处理附加的 I

lziog的专栏 1933

ROCm rocr-libhsakmt分析系列1-2:多GPU跨卡VRAM访问机制分析

本文基于ROCr-Runtime的libhsakmt库源码,深入分析了多GPU系统中跨设备内存访问的实现机制。主要内容包括: 实现流程概述:通过P2P拓扑发现、统一虚拟地址空间、页表映射和硬件转发四个步骤,实现零拷贝的跨GPU内存访问。

图形/计算GPU的驱动与虚拟化技术分享园地 2506

解决Windows 11文件右键菜单卡死问题的方法

当你右键点击一个文件或者文件夹时,系统会加载与该文件类型相关的上下文菜单项,如果其中某个菜单项的代码执行时间过长或者出现错误,就会导致整个右键菜单卡死。综上所述,当你在Windows 11中遇到右键菜单卡死的问题时,你可以尝试禁用上下文菜单项、重置文件关联、排除第三方软件干扰、修复系统文件以及优化代码等方法来解决问题。如果你是一个开发者,并且你自己编写了一个上下文菜单项,那么问题可能出在你的代码中。例如,如果你遇到了图片文件的右键菜单卡死问题,你可以找到".jpg"或".png"等相关的子项。

HnrzLinux的博客 7536

PCIe驱动开发-驱动加载

上篇中,我们探讨了PCIe设备是如何在Bios中被初始化的,Bios中各种准备工作做好后,就开始引导装载操作系统,系统的控制权移交给操作系统,操作系统中也要对系统中各PCI设备进行扫描以及初始化(和上文中提到的Bios对PCI设备的枚举和配置一样),事实上,早期的操作系统也是通过Bios调用的方式来获取系统PCI设备信息的,只不过现在变成亲自动手了。总体来说,操作系统对PCI的枚举以及初始化的过程

爱她就要努力 2745

最详细的 SAP ABAP Web Service 创建和消费步骤讲解

本文原版发表于 SAP 官方社区,当时用的英文版写作,标题为:Step by step to create, consume and trace web service in ABAP system https://blogs.sap.com/2014/05/20/step-by-step-to-create-consume-and-trace-web-service-in-abap-system/ 总共阅读量超过 18 万。 How to create web service provider in A

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 3151

MSDN Kernel-Mode Driver Architecture学习笔记(4)――Kernel-Mode Driver Components (3)

MSDN Kernel-Mode Driver Architecture学习笔记(4)――Kernel-Mode Driver Components (3) 2011年04月23日   研读MSDN,边学边做同步翻译日志。暂时有些语义不好理解,待通读一遍后会好的。^_^ 生活雅趣Blog:http://blog.163.com/hook_ttg/               ...

gogohulo 240

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

MSDN Kernel-Mode Driver Architecture学习笔记(4)——Kernel-Mode Driver Components (1)

本节介绍内核模式驱动程序包含的标准例程。这些标准驱动程序例程的一些是必需的,一些是可选。本节也介绍驱动程序对象,其包含了指向每个驱动程序标准例程的指针。

HOOK_TTG( Jamie Jiang )的专栏 1313

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

MSDN Kernel-Mode Driver Architecture学习笔记(4)——Kernel-Mode Driver Components (4)

驱动程序的DispatchCleanup例程用来处理带有IRP_MJ_CLEANUP的I/O功能代码的IRPs。

HOOK_TTG( Jamie Jiang )的专栏 1110

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

MSDN Kernel-Mode Driver Architecture学习笔记(4)——Kernel-Mode Driver Components (2)

每个驱动程序必须有一个DriverEntry例程,用来初始化驱动程序范围内的数据结构和资源。I/O管理器在加载驱动程序的时候调用DriverEntry例程。

HOOK_TTG( Jamie Jiang )的专栏 1679

MSDN Kernel-Mode Driver Architecture学习笔记(4)——Kernel-Mode Driver Components (3)

处理任何I/O请求包(IRP)开始于一个派遣例程,这个派遣例程是驱动程序提供来处理IRP主功能代码的(IRP_MJ_Xxx)。驱动程序的DriverEntry例程输出派遣例程的入口点到一个驱动程序的DRIVER_OBJECT结构的派遣表中。

HOOK_TTG( Jamie Jiang )的专栏 1102

完成I/O请求

完成I/O请求每个IRP都渴望被完成。在标准模型中,你至少有两种完成IRP的环境。DpcForIsr通常用于完成导致最近中断的IRP。派遣函数也可以在下面这两种情况下完成IRP: 如果请求是错误的(可以以容易的检测方式查明,例如要求打印机倒纸请求或卸载键盘请求),则派遣例程应以失败方式完成该请求并返回适当的出错代码。    如果请求要求得到的仅是派遣函数可以容易确定的信息(例如

Iqian1314的专栏 3527

应用层与驱动层通信DeviceIoControl

驱动层与应用层通信是通过DeviceIoControl, 首先驱动层要实现: pDriverObject->DriverUnload = MyDriverUnload; pDriverObject->MajorFunction[IRP_MJ_CREATE] = MyCreate; pDriverObject->MajorFunction[IRP_MJ_CLOSE] = MyClose;

youyudexiaowangzi的专栏 6033

驱动入口函数DriverEntry的简单概述

概述 每一个驱动都有一个驱动入口函数DriverEntry 当IO Manager记载驱动时,就会调用驱动入口函数DriverEntry 当添加驱动折别是,该驱动入口函数负责驱动初始化。 初始化任务主要有确定驱动使用对象,设置驱动系统资源。 驱动入口只有IRQL=PASSIVE_LEVEL级别的系统线程可以调用 驱动对象简介DRIVER_OBJECT I / O管理器为已安装和加载的每个驱动程序创...

yshshx的博客 5645

总结:使用IoMarkPending的原因及原理

 为了使系统吞吐量最大化,I/O管理器希望驱动程序推迟其耗时IRP的完成。驱动程序通过在某个派遣例程中调用IoMarkIrpPending函数并返回STATUS_PENDING来表示完成操作被推迟。(注意:以下讨论未加说明均指在操作被推迟情况下)I/O管理器的原始调用者通常希望在继续执行之前等待操作完成,所以I/O管理器在处理推迟完成时有下面类似的逻辑(不代表真正的Microsoft源代码

gxfan的专栏 4212

白话STATUS_PENDING

我们经常看见这样一段驱动的代码: IoCopyCurrentIrpStackLocationToNext( Irp ); IoSetCompletionRoutine( Irp, completionRoutine, &waitEvent, TRUE, TRUE,

sqqsongqiqi的专栏 2045

恢复SSDT使瑞星保护失效

#include #include #define IOCTL_SETPROC  (ULONG)CTL_CODE(FILE_DEVICE_UNKNOWN, 0x852, METHOD_NEITHER, FILE_READ_DATA | FILE_WRITE_DATA )NTSTATUS DisPatchCreateClose(PDEVICE_OBJECT pDriverObj,PIRP pIrp

1646

MSDN Kernel-Mode Driver Architecture学习笔记(3)——Device Objects and Device Stacks (3)

对于大多数中间层或者最底层的驱动程序,设备扩展是设备对象非常重要的数据结构。它的内部结构是由驱动程序定义的,通常用于: l 维护设备状态信息。 l 为任何内核定义的对象或者其他系统资源提供存储空间,比如自旋锁,驱动程序需要用到这些信息。 l 容纳任何驱动程序必须在系统空间常驻的用于执行其I/O操作的数据。

HOOK_TTG( Jamie Jiang )的专栏 2747
上一篇: MSDN Kernel-Mode Driver Architecture学习笔记(4)——Kernel-Mode Driver Components (4)
下一篇: 获取本机计算机名和IP地址
HOOKTTG
博客等级 码龄20年 25粉丝 29原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值