忆杰的博客

忆杰的博客

Irp超时处理

很多时候Irp被送到底层驱动程序后, 由于硬件设备的问题, Irp不能够得到及时的处理, 甚至有可能永远都不会被处理.这时候需要对Irp超时情况作出处理, 一旦在规定时间内Irp没有被处理, 操作系统就会进入到Irp的超时处理函数中.

在驱动程序编程中, 经常遇到一种情况, 对某一设备的操作很久没有反应, 如果在规定时间内没有完成操作, 则要取消该操作, 取消例程需要程序员发出取消命令. 如在应用程序中执行CancelIo Win32 api函数, 或者在驱动程序中执行IoCanceIrp内核函数.当然也可以设置Irp超时, 当Irp超时后, 操作系统页会取消Irp. 从而进入Irp的取消例程.

动作是这样的, 首先初始化一个定时器对象和一个Dpc对象, 并将Dpc例程和定时器对象进行关联, 在每次对Irp操作前.开启定时器, 并设置好一定时间的超时. 如果在指定时间内对irp的处理没有结束. 那么操作系统就会进入Dpc例程.还有如果驱动程序能在超时前结束Irp的操作. 则应该取消定时器. 从而保证不会再次取消Irp.

下面这个代码对写入超时处理, 为了演示超时的效果. 在Irp_Mj_Write中直接在派遣函数中直接返回Pending.这样Irp就永远不会被结束了. 在超时到了, 就会进入超时处理函数. 在超时处理函数中Irp被超时结束. 当然也打印了一下.
 
 

/*	Windows 内核下Irp超时处理 3环代码
	编译方法参见makefile. TAB = 8
*/
#include <windows.h>
#include <stdio.h>

#pragma comment( linker, "/Entry:Jmain" )
#pragma comment( linker, "/subsystem:console" )

#define DEVICE_NAME	"\\\\.\\SysLinkTimeOut"
//===========================================================================
//用户层入口
//===========================================================================
int Jmain() {
	CHAR cBuf[10];
	BOOL bRet;
	DWORD dwByteWrite;
	HANDLE hDevice = INVALID_HANDLE_VALUE;
	OVERLAPPED StOverLapped = {0};

	do {
		//异步打开设备
		hDevice = CreateFile( DEVICE_NAME, GENERIC_READ | GENERIC_WRITE,
				      0, NULL, OPEN_EXISTING,
				      FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL );
		if ( hDevice == INVALID_HANDLE_VALUE ) {
			printf( "打开设备驱动失败!\n" );
			break;
		}

		RtlFillMemory(cBuf, sizeof( cBuf ), 'a' );
		bRet = WriteFile( hDevice, cBuf, sizeof( cBuf ), &dwByteWrite, &StOverLapped );
		if ( !bRet ) {
			printf ( "写入设备失败!\n" ) ;
			break;
		}
	} while ( FALSE );

	if ( hDevice != INVALID_HANDLE_VALUE ) {
		CloseHandle( hDevice );
	}
	system( "pause" );
	return 0;
}

这边是内核的代码:
 

/*	Windows 内核下Irp超时处理 0环代码
	编译方法参见makefile. TAB = 8
*/
#include <ntddk.h>

#define DEVICE_NAME		L"\\Device\\DevTimeOut"
#define SYSLINK_NAME		L"\\??\\SysLinkTimeOut"

typedef struct tagDevice_Ext {
	PIRP pIrp;
	KDPC StDpc;		//Dpc对象
	KTIMER StTimer;		//计时器对象
	PDEVICE_OBJECT pDevice;
	UNICODE_STRING USzDeviceName;
	UNICODE_STRING USzSysLinkName;
} DEVICE_EXT, *PDEVICE_EXT;

//===========================================================================
//驱动卸载例程
//===========================================================================
VOID DriverUnload( PDRIVER_OBJECT pDriverObj ) {
	PDEVICE_EXT pDeviceExt = NULL;
	PDEVICE_OBJECT pNextDeviceObj = NULL;

	pNextDeviceObj = pDriverObj->DeviceObject;

	while ( pNextDeviceObj != NULL ) {

		pDeviceExt = pNextDeviceObj->DeviceExtension;

		IoDeleteDevice( pDeviceExt->pDevice );
		IoDeleteSymbolicLink( &pDeviceExt->USzSysLinkName );

		KdPrint( ( "删除%wZ成功!\n", &pDeviceExt->USzDeviceName ) );
		pNextDeviceObj = pNextDeviceObj->NextDevice;
	}
}
//===========================================================================
//所有不关心的Irp
//===========================================================================
NTSTATUS DispatchRoutine( PDEVICE_OBJECT pDeviceObj, PIRP pIrp ) {

	pIrp->IoStatus.Information = 0;
	pIrp->IoStatus.Status = STATUS_SUCCESS;
	IoCompleteRequest( pIrp, IO_NO_INCREMENT );

	return STATUS_SUCCESS;
}

//===========================================================================
//写入请求处理
//===========================================================================
NTSTATUS DispatchWrite( PDEVICE_OBJECT pDeviceObj, PIRP pIrp ) {
	LARGE_INTEGER liTimeOut;
	PDEVICE_EXT pDeviceExt = NULL;

	pDeviceExt = pDeviceObj->DeviceExtension;
	ASSERT( pDeviceExt != NULL );

	//将当前Irp挂起
	IoMarkIrpPending( pIrp );

	pDeviceExt->pIrp = pIrp;

	//定义3S超时
	liTimeOut = RtlConvertLongToLargeInteger( -10 * 3000 * 10 );

	//开启定时器, 3秒后Irp返回失败, 这里肯定是返回失败了!
	//如果驱动程序能够超时前结束Irp的操作, 则应该取消定时器从而保证
	//不会被操作系统再次取消Irp
	KeSetTimer( &pDeviceExt->StTimer, liTimeOut, &pDeviceExt->StDpc );

	return STATUS_PENDING;
}
//===========================================================================
//3秒DPC 取消例程
//===========================================================================
#pragma code_seg( )
VOID OnTimerDpc( PKDPC pDpc, PVOID pContext, PVOID SysArg1, PVOID SysArg2 ) {
	PIRP pIrp = NULL;
	ULONG ulWriteLen, i;
	PUCHAR pBuf = NULL;
	PDEVICE_EXT pDeviceExt = NULL;
	PDEVICE_OBJECT pDeviceObj = NULL;
	PIO_STACK_LOCATION Stack = NULL;

	PAGED_CODE_LOCKED();

	pDeviceExt = ( ( PDEVICE_OBJECT )pContext )->DeviceExtension;
	pIrp = pDeviceExt->pIrp;

	Stack = IoGetCurrentIrpStackLocation( pIrp );
	ulWriteLen = Stack->Parameters.Write.Length;
	pBuf = pIrp->AssociatedIrp.SystemBuffer;

	for( i = 0; i < 10; i++ ) {
		KdPrint( ( "%c\t", *( pBuf + i ) ) );
	}
	KdPrint( ( "\n" ) );

	//取消IRP, Win32那边会返回失败!
	pIrp->IoStatus.Information = 0;
	pIrp->IoStatus.Status = STATUS_CANCELLED;
	IoCompleteRequest( pIrp, IO_NO_INCREMENT );
}
//===========================================================================
//驱动入口
//===========================================================================
NTSTATUS DriverEntry( PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pUSzRegPath ) {
	NTSTATUS Status;
	ULONG i;
	PDEVICE_EXT pDeviceExt = NULL;
	PDEVICE_OBJECT pDevice = NULL;
	UNICODE_STRING USzDeviceName = RTL_CONSTANT_STRING( DEVICE_NAME );
	UNICODE_STRING USzSysLinkName = RTL_CONSTANT_STRING( SYSLINK_NAME );

	Status = IoCreateDevice( pDriverObj, sizeof( DEVICE_EXT ), &USzDeviceName,
				 FILE_DEVICE_UNKNOWN, 0, TRUE, &pDevice );
	if ( !NT_SUCCESS( Status ) ) {
		KdPrint( ( "设备创建失败!\n" ) );
		return Status;
	}

	Status = IoCreateSymbolicLink( &USzSysLinkName, &USzDeviceName );
	if ( !NT_SUCCESS( Status ) ) {
		KdPrint( ( "设备链接创建失败!\n" ) );
		return Status;
	}

	pDevice->Flags |= DO_BUFFERED_IO;
	pDeviceExt = pDevice->DeviceExtension;
	pDeviceExt->pDevice = pDevice;
	pDeviceExt->USzDeviceName = USzDeviceName;
	pDeviceExt->USzSysLinkName = USzSysLinkName;

	//初始化定时器对象, Dpc对象
	KeInitializeTimer( &pDeviceExt->StTimer );
	KeInitializeDpc( &pDeviceExt->StDpc, &OnTimerDpc, pDevice );

	for( i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++ ) {
		pDriverObj->MajorFunction[i] = &DispatchRoutine;
	}
	pDriverObj->DriverUnload = &DriverUnload;
	pDriverObj->MajorFunction[IRP_MJ_WRITE] = &DispatchWrite;

	return Status;
}

发表评论

您必须登录 才能进行评论。