异步操作设备

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

typedef struct _OVERLAPPED {
  ULONG_PTR Internal;
  ULONG_PTR InternalHigh;
  union {
    struct {
      DWORD Offset; 操作设备会指定一个偏移量,从设备的偏移量进行读取。该偏移量用一个64位整形表示,Offset就是偏移量的低32位整形
      DWORD OffsetHigh;OffsetHigh是偏移量的高32位整数。
          } DUMMYSTRUCTNAME;
    PVOID Pointer;
  } DUMMYUNIONNAME;
  HANDLE    hEvent; 这个事件用于该操作完成后通知应用程序。程序员可以初始化该事件为未激发,当操作设备结束后,即在驱动中调用IoCompleteRequest后,设置该设备为激发态。

} OVERLAPPED, *LPOVERLAPPED;

在使用OVERLAPPED结构前,要先对其内部清零,并且为其创建事件。
下面的代码演示了如何在应用程序中使用异步操作。

// VEHXXXX.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <windows.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{

	//打开指定设备  加上FILE_FLAG_OVERLAPPED标志 表示设备操作为异步的
	HANDLE hDevice=CreateFileA(	   "c:/d.txt",
								   GENERIC_READ | GENERIC_WRITE,
								   0,
								   0,
								   OPEN_EXISTING,
								   FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED ,//FILE_FLAG_OVERLAPPED表示使用异步打开
								   0
							   );

	if (hDevice==INVALID_HANDLE_VALUE)
	{
		printf("设备打开失败\n");
		getchar();
		return false;
	}

	OVERLAPPED Overlap={0};
	Overlap.hEvent=CreateEvent(0,0,0,0);
	UCHAR buffer[100];
	DWORD dwRead;
	ReadFile(hDevice,buffer,100,&dwRead,&Overlap);
	//使用异步操作,程序不会等待ReadFile执行完,而是继续往下执行
	printf("并行执行");
	WaitForSingleObject(Overlap.hEvent,INFINITE);
	CloseHandle(hDevice);
	getchar();
	return 0;
}

异步操作设备(方式二)
除了ReadFile 和WriteFile函数外,还有两个API也可以实现异步读写,这就是ReadFileEx
和WriteFileEx 函数。ReadFile和WriteFile既可以支持同步读写操作,又可以支持异步读
写操作。而ReadFileEx和WriteFileEx函数是专门用于异步读写操作的,不能进行同步读
写。首先看一下ReadFileEx的声明。

BOOL ReadFileEx(
  HANDLE                          hFile,	//设备句柄
  LPVOID                          lpBuffer,	//读取数据的缓冲区
  DWORD                           nNumberOfBytesToRead,	//读取的字节数
  LPOVERLAPPED                    lpOverlapped,	//一个OVERLAPPED指针
  LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine	//完成后会调用这个函数
);
// VEHXXXX.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <windows.h>
#include <stdlib.h>
VOID CALLBACK FileIoCompletionRoutine(
									    DWORD dwErrorCode,
										DWORD dwNumberOfBytesTransfered,
										LPOVERLAPPED lpOberLapped
									  )
{
	printf("回调函数被调用");

}

int main(int argc, char* argv[])
{

	//打开指定设备
	HANDLE hDevice=CreateFileA(	   "c:/d.txt",
								   GENERIC_READ | GENERIC_WRITE,
								   0,
								   0,
								   OPEN_EXISTING,
								   FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED ,//FILE_FLAG_OVERLAPPED表示使用异步打开
								   0
							   );

	if (hDevice==INVALID_HANDLE_VALUE)
	{
		printf("设备打开失败\n");
		getchar();
		return false;
	}

	OVERLAPPED Overlap={0};
	Overlap.hEvent=CreateEvent(0,0,0,0);
	UCHAR buffer[0x100]={0};

	//这里不需要OVERLAPPED结构里面的EVENT事件对象
	ReadFileEx(hDevice,buffer,0x100,&Overlap,FileIoCompletionRoutine);

	printf("%s",buffer);
	
	SleepEx(0,TRUE);
	CloseHandle(hDevice);
	
	getchar();
	return 0;
}


取消IRP

前面介绍了如何将“挂起”的IRP插入队列,并在关闭设备时,将“挂起”的IRP
结束。还有另外-一个办法可以将“挂起”的IRP逐个结束,这就是取消IRP请求。内核函
数IoSetCancelRoutine可以设置取消IRP请求的回调函数,其声明如下:

PDRIVER_CANCEL IoSetCancelRoutine(
	IN PIRP Irp;		//要取消的IRP
	IN PDRIVER_CANCEL CancelRoutine	//CancelRoutine:这个是取消函数的函数指针。一旦JIRP 请求被取消的
时候,操作系统会调用这个取消函数。

);

**调用该函数时,第二个参数为空,表明没有取消的回调函数**

程序员可以用IoCancellrp函数指定取消IRP请求。在IoCancellrp内部,需要进行同
步。DDK在IoCancellrp内部使用一个叫做cancel的自旋锁用来进行同步。
IoCancelIrp在内部会首先获得该自旋锁,loCancelIrp 会调用取消回调例程,因此,释
放该自旋锁的任务就留给了取消回调例程。获得取消自旋的函数是loAcquire
CancelSpinLock函数,而释放取消自旋锁的函数是IoReleaseCancelSpinLock 函数。

在应用程序中,可以调用Cancello Win32 API函数取消IRP请求。在Cancello的内部
会枚举所有没有被完成的IRP,然后依次调用loCancelrp。另外,如果应用程序没有调用
Cancello函数,应用程序在关闭设备时同样会自动调用Cancello。下面的代码演示了如何
编写取消例程。

VOID CancelReadIRP(IN PDEVICE_OBJECT pDeviceObject,
	IN PIRP pIrp
					
	)
{
	//IRP的完成状态设置成  STATUS_CANCELLED
	pIrp->IoStatus.Status = STATUS_CANCELLED;

	//IRP操作字节数设置为0
	pIrp->IoStatus.Information = 0;

	//完成请求
	IoCompleteRequest(pIrp,IO_NO_INCREMENT);

	//释放Cancel自旋锁  这个锁时是在IoCancelIrp中获得的,IoCancelIrp调用了此取消回调函数,所以需要在这里释放掉锁。
	IoReleaseCancelSpinLock(pIrp->CancelIrql);
}


//ReadFile IRP

NTSTATUS IrpReadProc(PDEVICE_OBJECT pDeviceObject/*设备信息*/, PIRP pIrp/*参数信息*/)
{

	//设置IRP的取消回调函数
	IoSetCancelRoutine(pIrp, CancelReadIRP);
	//告诉操作系统此Irp处于挂起状态 并没有处理。
	IoMarkIrpPending(pIrp);
	return STATUS_PENDING;
}

win32代码:

显示调用CancelIo,其实在关闭设备时(CloseHandle)会自动运行CancelIo
CancelIo(hDevice);  //hDevice打开的设备句柄
CloseHandle(hDevice);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值