windows驱动注册中断服务程序

2023-05-16

一个驱动程序的标准中断服务例程的必要功能和建立一个ISR的需求。

1.1 ISR需求

一个产生中断的物理设备的所有驱动程序必须有一个ISR。中断服务例程由内核定义如下:

BOOLEAN

( *PKSERVICE_ROUTINE )   (

      IN PKINTERRUPT Interrupt,

      IN PVOID ServiceContext

     );

ISR运行在DIRQL上,特别是在驱动程序用IoConnectInterrupt注册其ISR时说明的SynchronizeIrql层上。当驱动程序的ISR运行时,所有带一个中等或较低IRQL值的中断被当前处理器所屏蔽。

当然,另一个带有一个较高的系统分配的DIRQL的设备可以中断,或者一个高IRQL系统中断可以在任何时间发生在Windows NT/Windows 2000机器上。

记住以下情况:

§ 一个驱动程序的ISR是可中断的。

因为ISR运行在一个相对高的、系统分配的DIRQL上,因此在当前处理器上用一个中等或较低的IRQL屏蔽中断,ISR应尽可能快的返回控制。

DIRQL运行一个ISR限制了该ISR可以调用的支持例程。关于IRQL管理的更多信息,见第16章。关于任何特定支持例程都能被调用的IRQL的说明信息,见在线DDK。

1.1.1 ISR性能

Windows NT/Windows 2000在驱动程序ISR方面完全不同于其他一些操作系统。在Windows NT/Windows 2000系统上,如果其ISR能尽可能快的返回控制,而不是试图保持对CPU的控制并在其ISR中做尽可能多的I/O处理,尤其是在SMP机器上,那么驱动程序会有更好的表现。

反之,ISR应从中断处停止设备,并保存一切必要的关于导致中断的操作的状态信息或该操作的环境。ISR应在常驻内存中保存这些信息或环境,这类内存通常位于设备扩展中。这时,它应对驱动程序的DpcForIsr例程或一个CustomDpc排队以完成这个位于一个较低的IRQL(通常是IRQL DISPATCH_LEVEL)上的操作。

ISR返回一个Boolean,表明驱动程序的设备是否产生中断。对于共享一个中断向量或DIRQL的设备的驱动程序,每个ISR一旦确定其设备不是中断源,就应返回FALSE。

1.1.2 附加的需求的驱动程序例程

所有拥有一个ISR的驱动程序也必须拥有DpcForIsr或CustomDpc例程。驱动程序也可以有附加的CustomDpc例程,以用来完成特定的中断驱动的I/O操作。

如果任何驱动程序例程与驱动程序的ISR分享数据、设备寄存器或环境信息,该驱动程序还必须有一个或多个SynchCritSection例程。

ISR运行在比DpcForIsr或CustomDpc例程更高的IRQL上。因此,在一台Windows NT/Windows 2000单处理器的机器上,ISR必须在DpcForIsr或CustomDpc例程执行之前返回。当然,在一台SMP机器上,ISR和DpcForIsr(或CustomDpc)可以并行运行。

1.1.3 建立一个ISR

驱动程序通过在设备启动时调用IoConnectInterrupt注册其ISR。驱动程序在处理一个PnP IRP_MN_START_DEVICE请求时作为最终步骤应连接中断。

每个拥有一个ISR的驱动程序必须为至少一个中断对象指针提供常驻内存。通常,该指针被存放在代表产生中断的物理设备的设备对象的设备扩展中。如果驱动程序创建一个控制器对象,中断对象指针可以存放在控制器扩展中,或者它可以存放在由驱动程序分配的,非页式缓冲池中。

如果下列两者之一为真,驱动程序必须为中断自旋锁提供存储空间以连接其所有设备的所有中断对象:

§ 驱动程序有一个单独ISR为两个或多个设备处理不同向量的中断。

§ 驱动程序的ISR处理一个在多个向量上中断的设备

驱动程序在注册其ISR之前必须通过调用KeInitializeSpinLock初始化中断自旋锁。驱动程序也必须为它处理的、与中断对象指针一样多的IRQ提供存储区间。

1.2 ISR基本功能

在入口处,ISR被赋予一个指向驱动程序的中断对象的指针和一个指向驱动程序在调用IoConnectIntertupt时建立的任意区域的ServiceContext指针。大多数驱动程序设置ServiceContext指针以代表产生中断的物理设备的设备对象或者该设备对象的设备扩展。在设备扩展中,驱动程序可以为驱动程序的DpcForIsr例程设置状态信息,DpcForIsr例程通常进行几乎所有的I/O处理以满足每个导致设备中断的请求。

在没有重叠设备I/O操作的驱动程序中,ISR应做以下工作:

1.确定中断是否为假。如果是的话,立即返回FALSE以使中断设备的ISR迅速被调用。否则,继续中断处理。

2.从中断处停止设备。

3.收集所有DpcForIsr(或CustomDpc)例程需要用来完成为当前操作的I/O处理的环境信息。

4.存放该环境信息于DpcForIsr或CustomDpc例程可访问的区域,通常在处理当前导致中断的I/O请求的目标设备对象的设备扩展中。

5.如果驱动程序有一个DpcForIsr例程,用指向当前IRP、目标设备对象和存储的环境信息的指针调用IoRequestDpcIoRequestDpcDpcForIsr例程排队以便IRQL一低于处理器上的DISPATCH_LEVEL就运行。

如果驱动程序有一个CustomDpc例程,用一个指向DPC对象(与CustomDpc例程连接)的指针和指向任何保存的环境(CustomDpc例程将需要它来完成操作)的指针调用KeInsertQueueDpc。通常,ISR也传送指向当前IRP的指针与目标设备对象。一旦IRQL低于处理器上的DISPATCH_LEVEL ,CustomDpc例程便运行。

6.返回TRUE以表明其设备产生中断。

通常,一个ISR不做实际的I/O处理以满足一个IRP。相反,它从中断处停止设备,建立必要的状态信息,然后将驱动程序的DpcForIsr 或 CustomDpc排队以便进行任何满足当前导致设备中断请求的必要I/O处理。

考虑以下实现方针:

§ 为了获得可能最短的间隔,一个ISR必须运行于DIRQL

根据上述策略可以为机器中的所有设备增加I/O流量,因为运行于DIRQL屏蔽了所有系统已经分配了一个较低或中等IRQL值的中断。

驱动程序的中断对象的SynchronizeIrql(在驱动程序调用IoConnectInterrupt时被指定)确定驱动程序的ISR与SynchCritSection例程在其上运行的DIRQL。

当一个驱动程序的StartIo例程用驱动程序的SynchCritSection例程调用KeSynchronizeExecution时,该调用者也传送指向与ISR相连的中断对象的指针。因此,来自设备的中断被屏蔽在处理器运行的SynchCritSection例程上。与此同时,KeSynchronizeExecution持有与中断对象相连的中断自旋锁,以使ISR不能从另一个处理器访问设备寄存器或者设备扩展中共享的状态,直到驱动程序的SynchCritSection例程返回控制。

关于使用一个中断自旋锁的KeSynchronizeExecution的调用者的更多信息,见第16章的“使用自旋锁”。

1.3 ISR重叠I/O操作功能

当Windows NT/Windows 2000 SMP机器内的ISR和DpcForIsr(或CustomDpc)可以并行运行时,只有代表DpcForIsr或CustomDpc例程的DPC对象的一个实例可以为在任何给定的时刻执行而被排队。

如果相同DPC对象在DpcForIsr(或CustomDpc)例程运行之前通过一个ISR不止一次地排队,相关DpcForIsr或CustomDpc例程仅被调用一次。如果DPC对象在DpcForIsr(或CustomDpc)运行时排队,该例程的两个实例可以在Windows NT/Windows 2000 SMP机器内并行运行。

因此,任何在其设备上重叠I/O操作的驱动程序必须拥有DpcForIsr和/或CustomDpc例程,在这些例程被调用时它们可以完成多个IRP。对于驱动程序的ISR的基本要求与没有重叠I/O操作的设备驱动程序一样。见“ISR基本功能”。

当然,如果一个驱动程序重叠I/O操作,其ISR必须为DpcForIsr或CustomDpc例程设置附加状态。附加状态包括一个关于DPC例程需要完成而没有完成的请求的数量以及相关的环境信息。此外,如果ISR在DPC运行之前被调用以处理另一个中断,ISR必须小心不要覆盖为没有完成的请求存储的环境信息。

因为驱动程序的DPC例程与 ISR共享该状态,其DPC例程必须用一个系统提供的SynchCritSection例程调用KeSynchronizeExecution以访问代表每个DPC例程的共享状态。

关于这些例程的更多信息,见第9章“DpcForIsr和CustomDpc例程”。关于ISR和为ISR排队的DPC互动的更多信息,见第16章的“使用自旋锁”。

用DDK做的驱动中,中断为什么不能实现
我做的是PCI的驱动,用VC6 DDK来实现。板卡桥芯片用的是9052做的驱动中设置中断可是没有反应这是为什么呢?将9052的 LINTi1接了个开关,模拟实现中断的电平输入。相关程序如下:

//获取中断资源
case CmResourceTypeInterrupt:
irql = (KIRQL) resource->u.Interrupt.Level;
vector = resource->u.Interrupt.Vector;
affinity = resource->u.Interrupt.Affinity;
mode = (resource->Flags == CM_RESOURCE_INTERRUPT_LATCHED)? Latched : LevelSensitive;
irqshare = resource->ShareDisposition == CmResourceShareShared;


//连接中断
NTSTATUS status = IoConnectInterrupt(&pdx->InterruptObject, (PKSERVICE_ROUTINE) OnInterrupt,
(PVOID) pdx, NULL, vector, irql, irql, Latched, TRUE, affinity, FALSE);

//中断服务例程
BOOLEAN OnInterrupt(PKINTERRUPT InterruptObject, PDEVICE_EXTENSION pdx)
{ // OnInterrupt

//关中断
UCHAR HSR = READ_PORT_UCHAR(0x4c+pdx->portbase0);
KdPrint(("==============readHSRinterrupt!!!\n"));
HSR = HSR&0xFE;
WRITE_PORT_UCHAR(0x4c+pdx->portbase0,HSR);

KdPrint(("==============interrupt!!!\n"));

//恢复中断信号电平
//WRITE_REGISTER_UCHAR((PUCHAR)pdx->MemBar1+0x400000,0x10);
HSR = READ_PORT_UCHAR(0x4c+pdx->portbase0);
HSR = HSR|0x01;
WRITE_PORT_UCHAR(0x4c+pdx->portbase0,HSR);

IoRequestDpc(pdx->fdo, NULL, pdx);

return TRUE;
}

///WDMDeviceIOControl的事件响应
case IOCTL_ENABLE_INT:
{
//允许中断
UCHAR HSR = READ_PORT_UCHAR((PUCHAR)(0x4c+pdx->portbase0));
HSR = HSR | 0x01;

WRITE_PORT_UCHAR((PUCHAR)(0x4c+pdx->portbase0),HSR);

}
break;

//应用程序调用
DeviceIoControl(handle, IOCTL_ENABLE_INT, NULL, 0, NULL, 0, &dwReturn, NULL);

中断请求级(IRQL)

为了将不同CPU体系中不同的处理硬件优先级方法统一起来,NT使用了抽象的CPU优先级方案。即中断请求级。
IRQL是一个数,定义了CPU当前活动的重要性。
HIGHEST_LEVEL机器检查和总线错误(硬件)
POWER_LEVEL电源失效中断(硬件)
IPI_LEVEL多处理器系统处理器之间的门铃(硬件)
CLOCK2_LEVEL内部时钟2(硬件)
CLOCK1_LEVEL 内部时钟1(硬件)
PROFILE_LEVEL轮廓文件定时器(硬件)
DIRQLs IO设备中断的平台相关的级数(硬件)
DISPATCH_LEVEL线程调度器和延迟过程调用执行(软件)
DPC例程都是在IRQL=DISPATCH_LEVEL执行的,相当于ISR(中断服务例程)的一个延续,
伴随着ISR一起注册。
执行顺序:

执行I/O和中断------->ISR---------->DPC--------->I/O完成例程(IOCompleteRequest)--->APC(异步过程调用)

DPC对象从最终用户角度有两种:DpcForIsrCustomDPC。前者是与设备驱动对象(Device Object)绑定的;后者则由驱动自行维护。但从实现上来说,只有一种DPC对象存在,DpcForIsr所涉及的维护函数,实际上都是对CustomDPC的一个封装而已。
DPC是线程无关的,只有内核态的,这点不像APC
APC_LEVEL异步过程调用执行(软件)
PASSIVE_LEVEL一般的线程执行级(软件)
Dispatch例程的IRQLPASSIVE_LEVEL级别

看看下面的代码有什么问题?***_Read是一个Read例程。

NTSTATUS ***_Read(IN PDEVICE_OBJECT pFdo,IN PIRP Irp)

{

       ....

       KIRQL oldirql;

       …..

       // pdx->ReadBufferLock是在设备对象扩展里面的KSPIN_LOCK变量。

       KeAcquireSpinLock(&pdx->ReadBufferLock,&oldirql);

       ulBytesInReadBuffer = pdx->BytesInReadBuffer;

       KeReleaseSpinLock(&pdx->ReadBufferLock,oldirql);

       …..

}

通常Dispatch_readIRP_MJ_READ的处理例程,它运行在PASSIVE_LEVEL级别。但是当调用了KeAcquireSpinLock了之后,它就运行在DISPATCH_LEVEL级别了,这时是不能访问paged内存的。所以这段代码有可能会导致Windows蓝屏。

再看看下面这段代码会不会有问题?有什么问题?

NTSTATUS ***_DispatchClose(IN PDEVICE_OBJECT pFdo,IN PIRP Irp)

{

       …..

       KeAcquireSpinLock(&pdx->lkThread,&eOldIrqLevel);-

       if (pdx->thread)

       {

              ….

              KeWaitForSingleObject(pdx->thread,Executive,KernelMode,FALSE,NULL);

              ….

       }

       KeReleaseSpinLock(&pdx->lkThread, eOldIrqLevel)

…..

}

通常DispatchClose例程是运行IRQL等于PASSIVE_LEVEL,这是可以任意调用KeWaitForSingleObject,但是当调用KeAcquireSpinLock之后,IRQL升级到DISPATCH_LEVEL。这时再调用KeWaitForSingleObject来无限期等待,会导致dead lock,因为大家都是处在DISPATCH_LEVEL例程,别的例程可能得不到机会来设置KeWaitForSingleObject等待的事情,所以会导致dead lock

IoConnectInterrupt

IoConnectInterrupt的目的是为设备驱动程序注册一个ISR(中断服务例程),使得它可以再设备在指定的处理器上产生中断的时候被调用。

NTSTATUS

IoConnectInterrupt(

OUT PKINTERRUPT *InterruptObject,//指向驱动程序提供的中断对象存储地址,该参数随后要传递给KeSynchronizeExecution。

OUT PKSERVICE_ROUTINE ServiceRoutine,//中断服务例程的入口

IN PVOID ServiceContext,//指向驱动指定的即将传递给ISR的参数,ServiceContext必须在常驻内存中,可以是驱动程序创建的设备驱动的设备扩展,也可以是驱动创建的控制对象的控制拓展,还可以是设备驱动分配的非分页内存。

IN PKSPIN_LOCK SpinLock OPTIONAL,//指向已经初始化的自旋所,驱动程序负责自旋所的存储,并且该自旋所将用来同步被驱动程序其它例程共享的数据的访问,该参数在ISR处理多个中断向量或者驱动程序包含不止一个ISR时需要设置,否则,驱动程序不需要为中断自旋所分配存储空间,参数设置为NULL。

IN ULONG Vector,//输入获取的中断向量

IN KIRQL Irql,//输入获取的中断优先级DIRQL

IN KIRQL SynchronizeIrql,//指明ISR执行所在的DIRQL,当ISR需要处理多个中断向量或者驱动程序有多个ISR的时候,该值选择全部中断资源的u.Interrupt.Level中的最高值,否则和上面的Irql变量相等。

IN KINTERRUPT_MODE InterruptMode,//电平触发或者边沿触发

IN BOOLEAN ShareVector,//指明中断向量是否是可共享的。

IN KAFFINITY ProcessorEnableMask,//指定一个KAFFINITY值,用来说明设备中断可以在什么样的处理器平台上发生。

IN BOOLEAN FloatingSave //指明是否需要保存设备中断时的浮点堆栈,在X86平台下,该值必须是FALSE。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

windows驱动注册中断服务程序 的相关文章

随机推荐

  • python list转换字符串报错TypeError: sequence item 0: expected str instance, int found

    今天敲小例子 xff0c 报了错TypeError sequence item 0 expected str instance int found 小例子 xff1a list1 61 1 39 two 39 39 three 39 4 p
  • 初尝WSL(Windows Subsystem for Linux)

    微软的WSL发布也有一段时间了 xff0c 一直未尝试过 windows兼容linux子系统 xff0c 再联系最近微软windows部门整改 xff0c 不由感叹 由于工作是在windows环境下开发服务器程序 xff0c 对主流服务器操
  • SPI protocol 驱动编写 Part 1

    Linux 中 SPI 系统概览 Contents Part 1 Linux中 SPI子系统概览 Part 2 SPI message基础 Part 3 异步写 Overview SPI框架的内核文档是个好的开始 在你的内核源码中 Docu
  • Android知识梳理

    Android中5种布局 xff1a FrameLayout xff0c LinearLayout RelativeLayout TableLayout全部继承ViewGroup Activity和Fragment的关系 xff1a onA
  • 原型模式的使用场景

    文章目录 原型模式的使用场景源码使用场景 Intent案例demo优点缺点对比 原型模式的使用场景 1 类初始化需要消化非常多的资源 这个资源包括数据 硬件资源等 通过原型拷贝避免这些消耗 2 通过new一个对象需要非常繁琐的数据准备或访问
  • 好看的常用背景色RGB数值

  • Windows下小狼毫输入法(Rime)的安装与配置

    首先去官网 http rime im 下载小狼毫输入法的安装程序进行安装 xff1a 安装好后设置 xff0c 我只选择了 朙月拼音 和 朙月拼音简化字 两种输入法 xff0c xff08 一 xff09 繁体转简体 刚装好默认输入的是繁体
  • 在UEFI模式下,linux误删EFI分区后,重新恢复引导

    遇到上面情况 xff0c 我们通常使用boot repair修复引导 但是这时会弹出一个错误 xff1a GPT detected Please create a BIOS Boot partition 遇到这个情况以后 xff0c 我就疯
  • mysql 报错ERROR 1064 (42000),原因使用了mysql保留字

    执行select语句 xff1a select from cfg parameter where key 61 39 nSJtifqVSI7HkPrKHlxhD6 39 ERROR 1064 42000 You have an error
  • Unable to preventDefault inside passive event listener due to target being treated as passive.

    最近做项目经常在 chrome 的控制台看到如下提示 xff1a Unable to preventDefault inside passive event listener due to target being treated span
  • GBK 编码

    GBK编码范围 xff1a 8140 xff0d FEFE xff0c 汉字编码范围见第二节 xff1a 码位分配及顺序 GBK编码 xff0c 是对GB2312编码的扩展 xff0c 因此完全兼容GB2312 80标准 GBK编码依然采用
  • 子类能否重写父类的静态方法?

    今天在看到了一道面试题 xff0c 题目是一道代码阅读题 xff0c 问下面的代码输出结果是什么 xff1f 我最开始的理解 xff1a 上面的代码我们可以看到 xff0c 上面的类中有两个内部类Sub和Super xff0c Sub继承了
  • Blazor 从入门到放弃

    Blazor 从入门到放弃 Intro Blazor 是微软在 NET 里推出的一个 WEB 客户端 UI 交互的框架 xff0c 使用 Blazor 你可以代替 JavaScript 来实现自己的页面交互逻辑 xff0c 可以很大程度上进
  • WPF知识学习

    RelativeSource 61 RelativeSource AncestorType 61 x Type Window 是一种 WPF XAML 绑定方式 xff0c 它表示要从当前控件的父级元素中找到类型为 Window 的元素 x
  • C#表达式树解析步骤

    C 表达式树是一种将 C 代码表示为对象树的方式 xff0c 它提供了一种在运行时动态构建和执行代码的能力 表达式树可以用于构建 LINQ 查询 动态生成代码 ORM 框架等场景 表达式树的解析过程可以分为两个步骤 xff1a 构建表达式树
  • 关于ConstraintLayout自适应高度遇到的坑

    关于ConstraintLayout自适应高度遇到的坑 记录下来 android layout height 61 34 wrap content 34 为了缩减嵌套层及采用了ConstraintLayout作为dialog布局 但是发现d
  • FluentValidation使用示例

    FluentValidation 是一个 NET 平台下的验证库 xff0c 用于验证对象的属性是否符合预期的规则 它提供了一种简洁的方式来编写验证规则 xff0c 支持链式编程 xff0c 可以轻松地构建复杂的验证逻辑 在 NET 6 中
  • SQLServer创建索引的5种方法

    前期准备 xff1a span class hljs operator span class hljs keyword create span span class hljs keyword table span Employee ID s
  • 正则表达式(匹配第一个花括号)

    学习正则 xff0c 工作中使用正则让我对 有了新的认知 xff1a 正则中 匹配输入字符串的开始位置 xff0c 除非在 方括号表达式中使用 xff0c 此时表示不接受该字符集合 废话不多说 xff0c 直接看栗子吧 xff0c 如下图所
  • windows驱动注册中断服务程序

    一个驱动程序的标准中断服务例程的必要功能和建立一个ISR的需求 1 1 ISR需求 一个产生中断的物理设备的所有驱动程序必须有一个ISR 中断服务例程由内核定义如下 xff1a span class hljs built in BOOLEA