如果需要将IRP异步完成, 一般不会在处理函数中调用IoCompleteRequest函数, 因为调用IoCompleteRequest函数就意味着,该IRP请求处理完成了, 事实上很多时候是需要多次处理, 或者有其他需求的, 这边先谈谈将当前IRP挂起.
挂起意味着,是异步操作, 那么需要等待以后某个时机再处理, 当然这个时机问题的话, 以后会了解到, 目前需要解决的是挂起当前IRP的方法.
下面这个驱动的功能就是这样, 将所有的IRP都挂起, 在最后Win32这边调用CloseHandle的时候再统一处理, 完成那些IRP请求. 一种异步完成IRP的例子. 除了挂起还要取消什么的. 下一篇说.
主要说说这一篇的挂起操作. 挂起其实也很简单, 就是不调用IoCompleteRequest, 改为调用IoMarkIrpPending. 当然这些IRP需要自己用某种手段存放起来, 不然的话回头取就不知道怎么取出来了, 这边用的是系统提供的双向链表了. 调用IoMarkIrpPending以后返回STASTUS_PENDING那么, Win32调用函数这边就会返回ERROR_IO_PENDING, 当然这样并不代表有错误的, 只是IRP还没有完成而已..
这几天兴致不是很高啊, 上代码吧. 这边是用户层的.
/* |
Windows内核下挂起当前IRP, 在IRP_MJ_CLEANUP中统一完成 3环代码 |
编译方法参见makefile. TAB = 8 |
*/ |
#include <stdio.h> |
#include <windows.h> |
#pragma comment( linker, "/Entry:Jmain" ) |
#define SYS_LINK_NAME "\\\\.\\SysLinkPendingIrp" |
//=========================================================================== |
//线程等待函数 |
//=========================================================================== |
DWORD WINAPI ThreadProc( LPVOID lpParameter ) { |
HANDLE hEvent; |
hEvent = *( HANDLE * )lpParameter; |
printf ( "线程开始进入等待状态!\n" ); |
WaitForSingleObject( hEvent, INFINITE ); |
ExitThread( 0 ); |
} |
//=========================================================================== |
//用户层的启动函数 |
//=========================================================================== |
int __stdcall Jmain( int argc, char * argv[] ) { |
HANDLE hFile = 0; |
BOOL bRet; |
DWORD dwByteWrite; |
HANDLE hThread[2] = {0}; |
DWORD dwThreadId[2]; |
BYTE byBuf[10]; |
OVERLAPPED StOverLapped1 = {0}; |
OVERLAPPED StOverLapped2 = {0}; |
__try { |
//异步打开设备. 下面操作的函数都是异步的 |
hFile = CreateFile( SYS_LINK_NAME, GENERIC_READ | GENERIC_WRITE, |
0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 0 ); |
if ( hFile == INVALID_HANDLE_VALUE ) { |
printf ( "打开设备失败!\n" ); |
return -1; |
} |
//创建两个事件用于异步读取文件 |
StOverLapped1.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); |
StOverLapped2.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); |
//创建两个线程等待内核设置事件, 不然看不到效果 |
hThread[0] = CreateThread( NULL, 0, &ThreadProc, &StOverLapped1.hEvent, 0, &dwThreadId[0] ); |
if ( hThread[0] == NULL ) { |
printf ( "创建线程1失败!\n" ); |
return -1; |
} |
hThread[1] = CreateThread( NULL, 0, &ThreadProc, &StOverLapped2.hEvent, 0, &dwThreadId[1] ); |
if ( hThread[1] == NULL ) { |
printf ( "创建线程2失败!\n" ); |
} |
Sleep( 1000 ); |
//--------------------------------------------------------------------------- |
//向设备发送了两次写入请求, 都会被挂起. |
//--------------------------------------------------------------------------- |
RtlFillMemory( byBuf, sizeof ( byBuf ), 'a' ); |
bRet = WriteFile( hFile, byBuf, sizeof ( byBuf ), &dwByteWrite, &StOverLapped1 ); |
if ( !bRet && GetLastError() != ERROR_IO_PENDING ) { |
printf ( "写入设备失败\n" ); |
return -1; |
} |
RtlFillMemory( byBuf, sizeof ( byBuf ), 'b' ); |
bRet = WriteFile( hFile, byBuf, sizeof ( byBuf ), &dwByteWrite, &StOverLapped2 ); |
if ( !bRet && GetLastError() != ERROR_IO_PENDING ) { |
printf ( "写入设备失败\n" ); |
return -1; |
} |
if ( hFile ) { |
CloseHandle( hFile ); |
} |
//等待多个对象返回(必须同时返回) |
WaitForMultipleObjects( 2, hThread, TRUE, INFINITE ); |
printf ( "两个对象都已经返回!\n" ); |
//--------------------------------------------------------------------------- |
} __finally { |
if ( hThread[0] ) { |
CloseHandle( hThread[0] ); |
} |
if ( hThread[1] ) { |
CloseHandle( hThread[1] ); |
} |
if ( StOverLapped1.hEvent ) { |
CloseHandle( StOverLapped1.hEvent ); |
} |
if ( StOverLapped2.hEvent ) { |
CloseHandle( StOverLapped2.hEvent ); |
} |
if ( hFile ) { |
CloseHandle( hFile ); |
} |
system ( "pause" ); |
} |
return 0; |
} |
这边是内核态的代码:
/* |
Windows内核下挂起当前IRP, 在IRP_MJ_CLEANUP中统一完成 0环代码 |
编译方法参见makefile. TAB = 8 |
*/ |
#include <ntddk.h> |
#define DEVICE_NAME L"\\Device\\DevPendingIrp" |
#define SYS_LINK_NAME L"\\??\\SysLinkPendingIrp" |
//--------------------------------------------------------------------------- |
typedef struct tagDevice_Ext { |
PDEVICE_OBJECT pDeviceObj; |
PLIST_ENTRY pIrpListHead; //链表头结点, 用于存放IRP请求 |
UNICODE_STRING USzDeviceName; |
UNICODE_STRING USzSysLinkName; |
} DEVICE_EXT, *PDEVICE_EXT; |
typedef struct Irp_Entry { |
PIRP pIRP; |
LIST_ENTRY ListEntry; |
} IRP_ENTRY, *PIRP_ENTRY; |
//=========================================================================== |
//驱动卸载例程 |
//=========================================================================== |
#pragma code_seg( "PAGE" ) |
VOID DriverUnLoad( PDRIVER_OBJECT pDriverObj ) { |
PDEVICE_EXT pDeviceExt = NULL; |
PDEVICE_OBJECT pNextDevice = NULL; |
PAGED_CODE(); |
pNextDevice = pDriverObj->DeviceObject; |
while ( pNextDevice != NULL ) { |
pDeviceExt = pNextDevice->DeviceExtension; |
IoDeleteDevice( pDeviceExt->pDeviceObj ); |
IoDeleteSymbolicLink( &pDeviceExt->USzSysLinkName ); |
KdPrint( ( "删除%wZ设备成功!\n" , &pDeviceExt->USzDeviceName ) ); |
pNextDevice = pNextDevice->NextDevice; |
} |
} |
//=========================================================================== |
//所有不关心的IRP处理例程 |
//=========================================================================== |
NTSTATUS DispatchRoutine( PDEVICE_OBJECT pDeviceObj, PIRP pIrp ) { |
PAGED_CODE(); |
pIrp->IoStatus.Information = 0; |
pIrp->IoStatus.Status = STATUS_SUCCESS; |
IoCompleteRequest( pIrp, IO_NO_INCREMENT ); |
return STATUS_SUCCESS; |
} |
//=========================================================================== |
//IRP_MJ_WRITE的处理, 将所有的IRP都返回为pending状态 |
//=========================================================================== |
NTSTATUS DispatchWrite( PDEVICE_OBJECT pDeviceObj, PIRP pIrp ) { |
PDEVICE_EXT pDeviceExt = NULL; |
PIRP_ENTRY pIrpEntry = NULL; |
PAGED_CODE(); |
pDeviceExt = pDeviceObj->DeviceExtension; |
ASSERT( pDeviceExt != NULL ); |
pIrpEntry = ( PIRP_ENTRY )ExAllocatePool( PagedPool, sizeof ( IRP_ENTRY ) ); |
ASSERT ( pIrpEntry != NULL ); |
pIrpEntry->pIRP = pIrp; |
//插入队列 |
InsertHeadList( pDeviceExt->pIrpListHead, &pIrpEntry->ListEntry ); |
//将IRP设置为挂起 |
IoMarkIrpPending( pIrp ); |
KdPrint( ( "在IRP_MJ_WRITE请求中将IRP挂起!\n" ) ); |
//返回pending状态 |
return STATUS_PENDING; |
} |
//=========================================================================== |
//将未决的写入请求完成 |
//=========================================================================== |
NTSTATUS Write_CompleteRequest( PDEVICE_OBJECT pDeviceObj, PIRP pIrp ) { |
ULONG i; |
ULONG ulWriteLength; |
ULONG ulWriteOffset; |
PIO_STACK_LOCATION WriteStack = NULL; |
PAGED_CODE(); |
WriteStack = IoGetCurrentIrpStackLocation( pIrp ); |
//欲写入的长度, 偏移 |
ulWriteLength = WriteStack->Parameters.Write.Length; |
ulWriteOffset = ( ULONG )WriteStack->Parameters.Write.ByteOffset.QuadPart; |
for ( i = 0; i < ulWriteLength; i++ ) { |
KdPrint( ( "%c\t" , *( ( ( UCHAR * )pIrp->AssociatedIrp.SystemBuffer ) + i ) ) ); |
} |
KdPrint( ( "\n" ) ); |
//简单的完成IRP请求 |
pIrp->IoStatus.Status = STATUS_SUCCESS; |
pIrp->IoStatus.Information = ulWriteLength; |
IoCompleteRequest( pIrp, IO_NO_INCREMENT ); |
KdPrint( ( "写入请求处理完毕!\n" ) ); |
return STATUS_SUCCESS; |
} |
//=========================================================================== |
//IRP_MJ_CLEANUP IRP的处理, 将IRP_MJ_WRITE未决的IRP全部处理下 |
//=========================================================================== |
NTSTATUS DispatchCleanUp( PDEVICE_OBJECT pDeviceObj, PIRP pIrp ) { |
NTSTATUS Status; |
PDEVICE_EXT pDeviceExt = NULL; |
PIRP_ENTRY pIrpEntry = NULL; |
PLIST_ENTRY pListEntry = NULL; |
PAGED_CODE(); |
pDeviceExt = ( PDEVICE_EXT ) pDeviceObj->DeviceExtension; |
Status = STATUS_UNSUCCESSFUL; |
for ( ; !IsListEmpty( pDeviceExt->pIrpListHead ); pIrpEntry = NULL ) { |
pListEntry = RemoveTailList( pDeviceExt->pIrpListHead ); |
//获取IRP的数据指针 |
pIrpEntry = CONTAINING_RECORD( pListEntry, IRP_ENTRY, ListEntry ); |
if ( !pIrpEntry ) { |
KdPrint( ( "获取结点数据失败!\n" ) ); |
Status = STATUS_UNSUCCESSFUL; |
} else { |
//完成写入请求 |
Status = Write_CompleteRequest( pDeviceObj, pIrpEntry->pIRP ); |
if ( !NT_SUCCESS( Status ) ) { |
KdPrint( ( "完成写入请求时失败!\n" ) ); |
} |
ExFreePool( pIrpEntry ); |
KdPrint( ( "未决请求处理完成!\n" ) ); |
} |
} |
//处理IRP_MJ_CLEANUP自身的IRP请求 |
pIrp->IoStatus.Status = Status; |
pIrp->IoStatus.Information = 0; |
IoCompleteRequest( pIrp, IO_NO_INCREMENT ); |
return Status; |
} |
//=========================================================================== |
//驱动程序的入口函数 |
//=========================================================================== |
#pragma code_seg( "INIT" ) |
NTSTATUS DriverEntry( PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pUSzRegPath ) { |
ULONG i; |
NTSTATUS Status; |
PDEVICE_OBJECT pDevice = NULL; |
PDEVICE_EXT pDeviceExt = NULL; |
UNICODE_STRING USzDeviceName = RTL_CONSTANT_STRING( DEVICE_NAME ); |
UNICODE_STRING UszSysLink = RTL_CONSTANT_STRING( SYS_LINK_NAME ); |
Status = IoCreateDevice( pDriverObj, sizeof ( DEVICE_EXT ), &USzDeviceName, |
FILE_DEVICE_UNKNOWN, 0, TRUE, &pDevice ); |
if ( !NT_SUCCESS( Status ) ) { |
KdPrint( ( "创建设备失败!\n" ) ); |
return Status; |
} |
Status = IoCreateSymbolicLink( &UszSysLink, &USzDeviceName ); |
if ( !NT_SUCCESS( Status ) ) { |
IoDeleteDevice( pDevice ); |
KdPrint( ( "创建符号链接失败!\n" ) ); |
return Status; |
} |
//带缓冲区的IO方式 |
pDevice->Flags |= DO_BUFFERED_IO; |
pDeviceExt = pDevice->DeviceExtension; |
pDeviceExt->pDeviceObj = pDevice; |
pDeviceExt->USzDeviceName = USzDeviceName; |
pDeviceExt->USzSysLinkName = UszSysLink; |
//初始化链表头结点 |
pDeviceExt->pIrpListHead = ( PLIST_ENTRY )ExAllocatePool( PagedPool, sizeof ( LIST_ENTRY ) ); |
InitializeListHead( pDeviceExt->pIrpListHead ); |
//设置分发函数, 驱动卸载函数 |
for ( i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++ ) { |
pDriverObj->MajorFunction[i] = &DispatchRoutine; |
} |
pDriverObj->DriverUnload = &DriverUnLoad; |
pDriverObj->MajorFunction[IRP_MJ_WRITE] = &DispatchWrite; |
pDriverObj->MajorFunction[IRP_MJ_CLEANUP] = &DispatchCleanUp; |
return Status; |
} |