忆杰的博客

忆杰的博客

自定义StartIo

这个系统给的StartIo虽然好用, 但是很多时候我们还是需要使用自定义的StartIo例程的. 因为系统提供的只能使用一个队列.如果是我们自己建立的话, 可以建立多个队列. 灵活性高, 但是稍微复杂些. 其实要说复杂也不尽然, 用那个系统的时候感觉不是很好,因为封装的时候不给力, 还需要在我们自己的函数中帮系统做一些事情.我倒. 还是这个好, 想建立几个队列就几个队列. 而且代码也不是那么的复杂. 也就是几个函数的调用了!  

 

自定义的StartIo例程的话需要程序员自己维护Irp队列, 这样程序员的话可以维护多个队列, 分别对应不同的Irp请求. 首先要搞这个自定义StartIo的话, 要搞个结构KDEVICE_QUEUE, 放在设备扩展里面就可以了. 如果想维护多个, 那么多放几个结构就可以了. 在DriverEntry里面调用keInitializeDeviceQueue函数初始化队列.当然有队列的话, 需要插入, 删除. 插入是KeInsertDeviceQueue, 还有个KeRemoveDeviceQueue删除队列中的元素,.

 

当然在编写分发函数的时候, 在派遣例程中要调用IoMarkIrpPending挂起当前的Irp.然后准备将Irp进入队列, 当然在进入队列之前需要将当前IRQL提升至DISPATCH_LEVEL.这个需要记得, 不要忘记, 然后在调用KeInsertDeviceQueue函数的时候如果返回FALSE的话, 表示队列已经满了, 需要开始调用自定义的StartIo例程了. 下面的代码就是这样做的.

那么在自定义的StartIo例程中, 处理传进StartIo中的Irp, 然后枚举队列中的Irp.依次出队列, 处理!那么自定义的StartIo就那么多东西了, 看代码吧, 这边是用户态的代码:
 

 

/*	Windows 内核下StartIo例程试验 3环代码
	编译方法参见makefile. TAB = 8
*/
#include <stdio.h>
#include <windows.h>

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

#define DEVICE_NAME	"\\\\.\\SysLinkCustomStartIo"
//===========================================================================
//线程过程, 向设备发送15次, 写入请求
//===========================================================================
DWORD __stdcall ThreadProc( PVOID pContext ) {
	UCHAR ucBuf[10];
	ULONG i, j;
	DWORD dwByteWrite;
	BOOL bRet;
	OVERLAPPED StOverlapped[15] = {0};
	HANDLE hEvent[15] = {0};

	__try {
		for( i = 0; i < sizeof( StOverlapped ) / sizeof( StOverlapped[0] ); i++ ) {

			StOverlapped[i].hEvent = CreateEvent( NULL, FALSE, TRUE, NULL );
			if ( !StOverlapped[i].hEvent ) {
				printf( "创建同步事件失败!\n" );
				return -1;
			}
		}

		for( i = 0; i < sizeof( StOverlapped ) / sizeof( StOverlapped[0] ); i++ ) {
			hEvent[i] = StOverlapped[i].hEvent;
		}

		Sleep( 1000 );
		for( i = 0; i < sizeof( StOverlapped ) / sizeof( StOverlapped[0] ); i++ ) {

			RtlFillMemory( ucBuf, sizeof( ucBuf ), i + 'a' );

			bRet = WriteFile( *( PHANDLE )pContext, ucBuf, sizeof( ucBuf ),
					  &dwByteWrite, &StOverlapped[i]  );

			if ( !bRet && GetLastError() != ERROR_IO_PENDING ) {
				printf( "写入设备失败!\n" );
				return -1;
			} else {
				for( j = 0; j < sizeof( ucBuf ); j++ ) {
					printf( "%c\t", ucBuf[j] );
				}

				printf( "\n" );
			}
		}

		//这个复杂度加了一些中间有可能还会有取消IRP的出现
		//CancelIo(*(PHANDLE)pContext);
		WaitForMultipleObjects( sizeof( StOverlapped ) / sizeof( StOverlapped[0] ),
					&hEvent[0], TRUE, INFINITE );

		printf( "设备处理完毕!\n" );

	} __finally {
		for( i = 0; i < sizeof( hEvent ) / sizeof( hEvent[0] ); i++ ) {
			if ( StOverlapped[i].hEvent ) {
				CloseHandle( StOverlapped[i].hEvent );
			}
		}
	}

	return 0;
}
//===========================================================================
int Jmain( ) {
	HANDLE hDevice = NULL;
	HANDLE hThead[2] = {0};
	DWORD dwTemp;

	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;
		}

		hThead[0] = CreateThread( NULL, 0, &ThreadProc, &hDevice, 0, &dwTemp );

		if ( !hThead[0] ) {
			printf( "创建线程1失败!\n" );
			break;
		} else {
			printf( "创建线程1成功, 线程1已经开始运行!\n" );
		}

		hThead[1] = CreateThread( NULL, 0, &ThreadProc, &hDevice, 0, &dwTemp );

		if ( !hThead[1] ) {
			printf( "创建线程2失败!\n" );
			break;
		} else {
			printf( "创建线程2成功, 线程2已经开始运行!\n" );
		}

		printf( "主线程开始等待两个线程返回!\n" );

		WaitForMultipleObjects( 2, hThead, TRUE, INFINITE );
		printf( "两个线程都已经返回!\n" );

	} while ( FALSE );

//---------------------------------------------------------------------------
	if ( hDevice ) {
		CloseHandle( hDevice );
	}
	if ( hThead[0] ) {
		CloseHandle( hThead[0] );
	}
	if ( hThead[1] ) {
		CloseHandle( hThead[1] );
	}

	system( "pause" );

	return 0;
}

这边是内核态的代码:
 

/*      Windows 内核下自定义StartIo 3环代码
        编译方法参见makefile. TAB = 8
*/
#include <ntddk.h>

#define DEVICE_NAME	L"\\Device\\CustomStartIo"
#define SYSLINK_NAME	L"\\??\\SysLinkCustomStartIo"

typedef struct tagDevice_Ext {
	KDEVICE_QUEUE DeviceQueue;		//设备队列
	PDEVICE_OBJECT pDeviceObj;
	UNICODE_STRING USzDeviceName;
	UNICODE_STRING USzSysLinkName;
} DEVICE_EXT, *PDEVICE_EXT;
//===========================================================================
//驱动卸载例程
//===========================================================================
#pragma code_seg( "PAGE" )
VOID DriverUnload( PDRIVER_OBJECT pDriverObj ) {
	PDEVICE_EXT pDeviceObj;
	PDEVICE_OBJECT pNextDeviceObj = NULL;

	pNextDeviceObj = pDriverObj->DeviceObject;

	while ( pNextDeviceObj != NULL ) {
		pDeviceObj = pNextDeviceObj->DeviceExtension;

		IoDeleteDevice( pDeviceObj->pDeviceObj );
		IoDeleteSymbolicLink( &pDeviceObj->USzSysLinkName );

		KdPrint( ( "删除%wZ设备成功!\n", &pDeviceObj->USzDeviceName ) );

		pNextDeviceObj = pNextDeviceObj->NextDevice;
	}
}
//===========================================================================
//所有不关心的Irp处理例程
//===========================================================================
#pragma code_seg( "PAGE" )
NTSTATUS DispatchRoutine( PDEVICE_OBJECT pDeviceObj, PIRP pIrp ) {

	pIrp->IoStatus.Information = 0;
	pIrp->IoStatus.Status = STATUS_SUCCESS;

	IoCompleteRequest( pIrp, IO_NO_INCREMENT );
	return STATUS_SUCCESS;
}
//===========================================================================
//自定义的StartIo例程
//===========================================================================
#pragma code_seg( )
VOID CustomStartIo( PDEVICE_OBJECT pDeviceObj, PIRP pIrp ) {
	PIRP NewIrp = NULL;
	KEVENT Event;
	ULONG ulWriteLen, i;
	LARGE_INTEGER liTimeOut;
	PDEVICE_EXT pDeviceExt = NULL;
	PIO_STACK_LOCATION Stack = NULL;
	PKDEVICE_QUEUE_ENTRY pQueue = NULL;

	PAGED_CODE_LOCKED();

	pDeviceExt = ( PDEVICE_EXT )pDeviceObj->DeviceExtension;
	NewIrp = pIrp;

	do {

//---------------------------------------------------------------------------
		//打印用户层传递的数据
//---------------------------------------------------------------------------
		KeInitializeEvent( &Event, NotificationEvent, FALSE );

		//等3秒
		liTimeOut.QuadPart = -3 * 1000 * 1000 * 10;

		//定义一个3秒的延时,主要是为了模拟该IRP操作需要大概3秒左右时间
		KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, &liTimeOut );

		Stack = IoGetCurrentIrpStackLocation( NewIrp );

		ulWriteLen = Stack->Parameters.Write.Length;

		for( i = 0; i < ulWriteLen; i++ ) {
			KdPrint( ( "%c\t",
				   *( ( UCHAR* )( NewIrp->AssociatedIrp.SystemBuffer ) ) ) );
		}
		KdPrint( ( "\n" ) );
//---------------------------------------------------------------------------
		NewIrp->IoStatus.Status = STATUS_SUCCESS;
		NewIrp->IoStatus.Information = 0;
		IoCompleteRequest( NewIrp, IO_NO_INCREMENT );
		KdPrint( ( "CustomStartIo调用一次:%x\n", pQueue ) );

		//将存放的Irp出队列
		pQueue = KeRemoveDeviceQueue( &pDeviceExt->DeviceQueue );

		//一直处理到队列为空为止
		if ( pQueue == NULL ) {
			break;
		}

		NewIrp = CONTAINING_RECORD( pQueue, IRP,
					    Tail.Overlay.DeviceQueueEntry );
	} while( TRUE );
}
//===========================================================================
//取消例程
//===========================================================================
VOID OnCancelIRP( PDEVICE_OBJECT DeviceObject, PIRP Irp  ) {

	//释放Cancel自旋锁
	IoReleaseCancelSpinLock( Irp->CancelIrql );

	//设置完成状态为STATUS_CANCELLED
	Irp->IoStatus.Status = STATUS_CANCELLED;
	Irp->IoStatus.Information = 0;
	IoCompleteRequest( Irp, IO_NO_INCREMENT );

	KdPrint( ( "离开取消例程\n" ) );
}
//===========================================================================
//写入请求处理
//===========================================================================
#pragma code_seg( "PAGE" )
NTSTATUS DispatchWrite( PDEVICE_OBJECT pDeviceObj, PIRP pIrp ) {
	KIRQL OldIrql;
	BOOLEAN bRet;
	PDEVICE_EXT pDeviceExt = NULL;

	PAGED_CODE();

	pDeviceExt = ( PDEVICE_EXT )pDeviceObj->DeviceExtension;

	//将IRP设置为挂起
	IoMarkIrpPending( pIrp );

	//设置取消例程
	IoSetCancelRoutine( pIrp, OnCancelIRP );

	//提升IRP至DISPATCH_LEVEL
	KeRaiseIrql( DISPATCH_LEVEL, &OldIrql );

	KdPrint( ( "DispatchWrite设备扩展指针:%x\n",
		   &pIrp->Tail.Overlay.DeviceQueueEntry ) );

	//如果KeInsertDeviceQueue返回FALSE的时候,表明Irp没有插入到
	//队列, 而是需要立即被结束
	bRet = KeInsertDeviceQueue( &pDeviceExt->DeviceQueue,
				    &pIrp->Tail.Overlay.DeviceQueueEntry );
	if ( !bRet ) {
		KdPrint( ( "调用CustomStartIo例程!\n" ) );

		//调用自定义StartIo例程.
		CustomStartIo( pDeviceObj, pIrp );
	}

	//将IRP降至原来IRQL
	KeLowerIrql( OldIrql );

	//返回pending状态
	return STATUS_PENDING;
}
//===========================================================================
//驱动入口
//===========================================================================
#pragma code_seg( "INIT" )
NTSTATUS DriverEntry( PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegPath ) {
	ULONG i;
	NTSTATUS Status;
	PDEVICE_EXT pDeviceExt = NULL;
	PDEVICE_OBJECT pDeviceObj = 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, &pDeviceObj );
	if ( !NT_SUCCESS( Status ) ) {
		KdPrint( ( "创建设备失败!\n" ) );
		return Status;
	}

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

	//设置设备属性, 设备扩展
	pDeviceObj->Flags |= DO_BUFFERED_IO;
	pDeviceExt = pDeviceObj->DeviceExtension;
	pDeviceExt->pDeviceObj = pDeviceObj;
	pDeviceExt->USzDeviceName = USzDeviceName;
	pDeviceExt->USzSysLinkName = USzSysLinkName;

	//初始化设备队列(自己实现StartIo)
	RtlZeroMemory( &pDeviceExt->DeviceQueue, sizeof( pDeviceExt->DeviceQueue ) );
	KeInitializeDeviceQueue( &pDeviceExt->DeviceQueue );

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

	return Status;
}

网友评论:

  1. 雪莱 说:

    兄弟,CustomStartIo 和 OnCancelIRP 分别都结束了IRP(IoCompleteRequest( Irp, IO_NO_INCREMENT )) ,重复调用会崩溃的 ~

发表评论

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