<学习笔记>Windows驱动开发技术详解__IRP的同步

对设备的任何操作都会最终转化为IRP请求,而IRP一般都是由操作系统异步发送的。异步处理IRP有助于提高效率,但是有时异步处理会带来逻辑上的错误,这时需要将异步的IRP同步化。将IRP同步化的方法有StartIO例程,使用中断服务例程等。


应用程序对设备的同步异步操作

大部分IRP都是由应用程序的Win32 API函数发起的。这些Win32 API本身就支持同步和异步的操作。例如:ReadFile,WriteFile和DeviceIoControl等,这些都有两种操作方式。一种同步,一种异步。


1.同步操作和异步操作的原理

操作设备的Win32 API主要是三个函数,即ReadFile函数,WriteFile函数,DeviceIOControl函数。以DeviceIOControl函数为例,当应用程序调用DeviceIoControl函数时,它的内部会创建一个IRP_MJ_DEVICE_CONTROL类型的IRP,并将这个IRP传送到驱动程序的派遣函数中。处理该IRP需要一段时间,直到IRP处理完毕后,DeviceIOControl函数才会返回。

同步操作时,在DeviceIOControl的内部,会调用WaitForSingleObject函数去等待一个事件。这个事件直到IRP被结束时,才会被触发。如果通过反汇编IoCompleteRequest内核函数,就会发现IoComplpeteRequest内部设置了该事件。DeviceIOControl会暂时进入睡眠状态,直到IRP被结束。

而对于异步操作的情况下,当DeviceIOControl被调用时,其内部会产生IRP,并将IRP传递给驱动程序的内部派遣函数。但此时DeviceIOControl不会等待该IRP的结束,而是直接返回。当IRP经过一段时间被结束时,操作系统会触发一个IRP相关事件。这个事件可以通知应用程序IRP请求被执行完毕。


2.同步操作设备

如果需要同步操作设备,在打开设备的时候就要制定以“同步”的方式打开设备。打开设备用CreateFile函数,其函数声明如下:

HANDLE
  CreateFile(
                LPCSTR lpFileName,                                  //设备名
		DWORD dwDesiredAccess,                              //访问权限
		DWORD dwShareMode,                                  //共享模式
		LPSECURITY_ATTRIBUTES lpSecurityAttributes,         //安全属性
		DWORD dwCreationDisposition,                        //如何创建
		DWORD dwFlagsAndAttributes,                         //设备属性
		HANDLE hTemplateFile                                //文件模板
	);

其中第六个参数dwFlagsAndAttributes是同步异步操作的关键。如果这个参数没有设置FILE_FLAG_OVERLAPPED,则以后对该设备的操作都是同步操作,否则都是异步操作。

对设备的操作Win32 API,例如ReadFile,WriteFile和DeviceIOControl函数,都会提供一个OVERLAP参数,如ReadFile函数:

BOOL ReadFile(
	HANDLE hFile,
	LPVOID lpBuffer,
	DWORD nNumberOfBytesToRead, 
	LPDWORD lpNumberOfBytesRead,
	LPOVERLAPPED lpOverlapped 
);

在同步操作设备时,其lpOverlapped参数设置为NULL。下面代码演示了应用程序如何对设备进行同步读取:

#include <windows.h>
#include <stdio.h>

#define BUFFER_SIZE	512
int main()
{
	HANDLE hDevice = 
		CreateFile("test.dat",
					GENERIC_READ | GENERIC_WRITE,
					0,
					NULL,
					OPEN_EXISTING,
					FILE_ATTRIBUTE_NORMAL,//此处没有设置FILE_FLAG_OVERLAPPED
					NULL );

	if (hDevice == INVALID_HANDLE_VALUE) 
	{
		printf("Read Error\n");
		return 1;
	}

	UCHAR buffer[BUFFER_SIZE];
	DWORD dwRead;
	ReadFile(hDevice,buffer,BUFFER_SIZE,&dwRead,NULL);//这里没有设置OVERLAP参数

	CloseHandle(hDevice);

	return 0;
}


3.异步操作设备(方式一)

异步操作设备时主要需要OVERLAP参数,Windows中用一种数据结构OVERLAPPED表示。

typedef struct _OVERLAPPED
{
	ULONG_PTR Internal;
	ULONG_PTR InternalHigh;
	DWORD Offset;
	DWORD OffsetHigh;
	HANDLE hEvent;
}OVERLAPPED;

第三个参数Offset:操作设备会指定一个偏移量,从设备的偏移量进行读取。该偏移量用一个64位整型表示,Offset就是偏移量的低32位。

第四个参数OffsetHigh是偏移量的高32位。

第五个参数hEvent:这个事件用于该操作完成后通知应用程序。程序员可以初始化该事件为未激发,当操作设备结束后,即在驱动程序中调用IoCompleteRequest后,设置该事件为激发态。

在使用OVERLAPPED结构前,要先对其内部清零,并为其创建事件。

下面代码演示如何在应用程序中使用异步操作:

#include <windows.h>
#include <stdio.h>

#define BUFFER_SIZE	512
//假设该文件大于或等于BUFFER_SIZE

#define DEVICE_NAME	"test.dat"
int main()
{
	HANDLE hDevice = 
		CreateFile("test.dat",
					GENERIC_READ | GENERIC_WRITE,
					0,
					NULL,
					OPEN_EXISTING,
					FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,//此处设置FILE_FLAG_OVERLAPPED
					NULL );

	if (hDevice == INVALID_HANDLE_VALUE) 
	{
		printf(&
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目录 第1篇 入门篇 第1章 从两个最简单的驱动谈起 1.1 DDK的安装 1.2 第一个驱动程序HelloDDK的代码分析 1.2.1 HelloDDK的头文件 1.2.2 HelloDDK的入口函数 1.2.3 创建设备例程 1.2.4 卸载驱动例程 1.2.5 默认派遣例程 1.3 HelloDDK的编译和安装 1.3.1 用DDK环境编译HelloDDK 1.3.2 用VC集成开发环境编译HelloDDK 1.3.3 HelloDDK的安装 1.4 第二个驱动程序HelloWDM的代码分析 1.4.1 HelloWDM的头文件 1.4.2 HelloWDM的入口函数 1.4.3 HelloWDM的AddDevice例程 1.4.4 HelloWDM处理PNP的回调函数 1.4.5 HelloWDM对PNP的默认处理 1.4.6 HelloWDM对IRP_MN_REMOVE_DEVICE的处理 1.4.7 HelloWDM对其他IRP回调函数 1.4.8 HelloWDM的卸载例程 1.5 HelloWDM的编译和安装 1.5.1 用DDK编译环境编译HelloWDM 1.5.2 HelloWDM的编译过程 1.5.3 安装HelloWDM 1.6 小结 第2章 Windows操作驱动的基本概念 第3章 Windows驱动编译环境配置、安装及调试 第4章 驱动程序的基本结构 第5章 Windows内存管理 第6章 Windows内核函数 第7章 派遣函数 第2篇 进阶篇 第8章 驱动程序的同步处理 第9章 IRP同步 第10章 定时器 第11章 驱动程序调用驱动程序 第12章 分层驱动程序 第13章 让设备实现即插即用 第14章 电源管理 第3篇 实用篇 第15章 I/O端口操作 第16章 PCI设备驱动 第17章 USB设备驱动 第18章 SDIO设备驱动 第19章 虚拟串口设备驱动 第20章 摄像头设备驱动程序 第4篇 提高篇 第21章 再论IRP 第22章 过滤驱动程序 第23章 高级调试技巧
这是书的光盘。共分为两个部分,这是第一部分。 本书由浅入深、循序渐进地介绍了Windows驱动程序的开发方法与调试技巧。本书共分23章,内容涵盖了 Windows操作系统的基本原理、NT驱动程序与WDM驱动程序的构造、驱动程序中的同步异步处理方法、驱 动程序中即插即用功能、驱动程序的各种调试技巧等。同时,还针对流行的PCI驱动程序、USB驱动程序 、虚拟串口驱动程序、摄像头驱动程序、SDIO驱动程序进行了详细的介绍,本书最大的特色在于每一节 的例子都是经过精挑细选的,具有很强的针对性。力求让读者通过亲自动手实验,掌握各类Windows驱动 程序的开发技巧,学习尽可能多的Windows底层知识。   本书适用于中、高级系统程序员,同时也可用做高校计算机专业操作系统实验课的补充教材。 原创经典,威盛一线工程师倾力打造。深入驱动核心,剖析操作系统底层运行机制,通过实例引导,快 速学习编译、安装、调试的方法。   从Windows最基本的两类驱动程序的编译、安装、调试入手讲解,非常容易上手,用实例详细讲解 PCI、USB、虚拟串口、虚拟摄像头、SDIO等驱动程序的开发,归纳了多种调试驱动程序的高级技巧,如 用WinDBG和VMWARE软件对驱动进行源码级调试,深入Windows操作系统的底层和内核,透析Windows驱动 开发的本质。 本书是作者结合教学和科研实践经验编写而成的,不仅详细介绍了Windows内核原理,而且介绍了编程技 巧和应用实例,兼顾了在校研究生和工程技术人员的实际需求,对教学、生产和科研有现实的指导意义 ,是一本值得推荐的专著。              ——中国工程院院士   院士推荐   目前,电子系统设计广泛采用通用操作系统,达到降低系统的设计难度和缩短研发周期。实现操作 系统与硬件快速信息交换是电子系统设计的关键。   通用操作系统硬件驱动程序的开发,编写者不仅需要精通硬件设备、计算机总线,而且需要Windows 操作系统知识以及调试技巧。学习和掌握Windows硬件驱动程序的开发是电子系统设计人员必备的能力。   本书是作者结合教学和科研实践经验编写而成的,不仅详细介绍了Windows内核原理,并且介绍了编 程技巧和应用实例,兼顾了在校研究生和工程技术人员的实际需求,对教学、生产和科研有现实的指导 意义,是一本值得推荐的专著。 第1篇 入门篇 第1章 从两个最简单的驱动谈起 本章向读者呈现两个最简单的Windows驱动程序,一个是NT式的驱动程序,另一个是WDM式的驱动程序。 这两个驱动程序没有操作具体的硬件设备,只是在系统里创建了虚拟设备。在随后的章节中,它们会作 为基本驱动程序框架,被本书其他章节的驱动程序开发所复用。笔者将带领读者编写代码、编译、安装 和调试程序。   1.1 DDK的安装   1.2 第一个驱动程序HelloDDK的代码分析    1.2.1 HelloDDK的头文件    1.2.2 HelloDDK的入口函数    1.2.3 创建设备例程    1.2.4 卸载驱动例程    1.2.5 默认派遣例程   1.3 HelloDDK的编译和安装    1.3.1 用DDK环境编译HelloDDK    1.3.2 用VC集成开发环境编译HelloDDK    1.3.3 HelloDDK的安装   1.4 第二个驱动程序HelloWDM的代码分析    1.4.1 HelloWDM的头文件    1.4.2 HelloWDM的入口函数    1.4.3 HelloWDM的AddDevice例程    1.4.4 HelloWDM处理PNP的回调函数    1.4.5 HelloWDM对PNP的默认处理    1.4.6 HelloWDM对IRP_MN_REMOVE_DEVICE的处理    1.4.7 HelloWDM对其他IRP回调函数    1.4.8 HelloWDM的卸载例程   1.5 HelloWDM的编译和安装    1.5.1 用DDK编译环境编译HelloWDM    1.5.2 HelloWDM的编译过程    1.5.3 安装HelloWDM   1.6 小结  第2章 Windows操作驱动的基本概念  驱动程序被操作系统加载在内核模式下,它与Windows操作系统内核的其他组件进行密切交互。本章主 要介绍Windows操作系统内核的基本概念,同时还介绍应用程序和驱动程序之间的通信方法。   2.1 Windows操作系统概述    2.1.1 Windows家族    2.1.2 Windows特性    2.1.3 用户模式和内核模式    
WDF驱动向底层磁盘驱动发送IRP_MJ_READ请求读取原始数据的示例代码如下: ```cpp NTSTATUS ReadFromDisk(WDFDEVICE Device, PVOID Buffer, ULONG Length) { NTSTATUS status = STATUS_SUCCESS; WDFREQUEST request = NULL; PIRP irp = NULL; KEVENT event; IO_STATUS_BLOCK ioStatusBlock; // 初始化事件 KeInitializeEvent(&event, NotificationEvent, FALSE); // 创建请求对象 status = WdfRequestCreate(WDF_NO_OBJECT_ATTRIBUTES, WdfDeviceGetIoTarget(Device), &request); if (!NT_SUCCESS(status)) { return status; } // 获取IRP对象 irp = WdfRequestWdmGetIrp(request); // 设置IRP参数 IoSetCompletionRoutine(irp, ReadCompletionRoutine, &event, TRUE, TRUE, TRUE); irp->Flags |= IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER; irp->AssociatedIrp.SystemBuffer = Buffer; irp->MdlAddress = NULL; irp->IoStatus.Status = STATUS_NOT_SUPPORTED; irp->IoStatus.Information = 0; irp->Tail.Overlay.Thread = PsGetCurrentThread(); irp->Tail.Overlay.OriginalFileObject = NULL; irp->Tail.Overlay.AuxiliaryBuffer = NULL; irp->Flags &= ~(IRP_PAGING_IO | IRP_NOCACHE | IRP_SYNCHRONOUS_API); // 设置IO栈位置 PIO_STACK_LOCATION irpStack = IoGetNextIrpStackLocation(irp); irpStack->MajorFunction = IRP_MJ_READ; irpStack->Parameters.Read.Length = Length; irpStack->Parameters.Read.ByteOffset.QuadPart = 0; // 发送IRP请求 status = WdfRequestSend(request, WdfDeviceGetIoTarget(Device), WDF_NO_SEND_OPTIONS); if (!NT_SUCCESS(status)) { WdfRequestComplete(request, status); return status; } // 等待请求完成 KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); // 获取IO状态 status = irp->IoStatus.Status; // 完成请求 WdfRequestComplete(request, status); return status; } ``` 在以上代码中,我们创建了一个WDF请求对象,并获取了其对应的IRP对象,然后设置了IRP的各项参数,最终发送请求并等待请求完成。一旦请求完成,我们就可以通过IRP的IoStatus成员获取IO操作的状态,然后使用WdfRequestComplete函数完成请求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值