忆杰的博客

忆杰的博客

缓冲区设备模拟文件读写

原来以为写Win32程序基于消息处理, 原来内核里面也是一个套路, 也差不多. 不过内核里面和这个消息肯定是有些区别的, 不过既然款式是一样的, 那么也就基本上可以等同理解了, 反正IO管理器会给你发送IRP, 然后你自己处理好这些个IRP, 那么基本上就OK了, 和Win32发送消息给你一样. 自己处理完返回系统. 看来MicroSoft那帮人还是比较暴力的..


 

在Windwos中有一种数据结构叫做IRP(输入输出请求包),这个东西和Win32下的消息非常的类似,那么既然和消息很类似,Win32上可以模拟消息, 也可以处理很多消息. 当然也可以发送消息. 既然是差不多. 那么内核里面肯定也是有这些个套路的,就是以前几个经常使用的API函数CreateFile, ReadFile, WriteFile, 原来以前一直被这几个函数给欺骗了, 这些个函数不只是可以打开文件, 还有很大用处, 莫非这个和Unix上那个把所有的设备撒的都当成文件处理有关系?这几个函数如果去打开我们创建出来的设备基本上就是转变为几种IRP请求了. 然后自己处理这些个请求. 处理请求的方式很多. 很多.慢慢来一种一种的写..
 
另外一个想想就缺少点什么, 在ReadFile和WriteFile的时候, 不是会写入一些个数据, 和读取一些个数据的么. 那么这个应该如何处理呢?其实有3种处理方式, 一种是操作系统给你把Win32提供的缓冲区给Copy一份,在内核中返回数据以后再Copy一次给Win32这边, 所以很慢..还有一种是直接使用分页机制, 将相同的缓冲区页面映射到同一个物理地址上, 这个相对是比较快了, 因为没有做内存的COPY, 但是还有一种暴力的方式, 是直接不做任何的处理, 将Win32缓冲区的地址传递给内核中的驱动程序, 那么这个很容易就导致崩溃了, 因为驱动在处理IRP的时候很有可能中断级不是很高. 然后会被任务切换给打断, 回头再跑这个代码, 已经处在另外一个进程的地址空间中了, 导致访问那个地址也被换了,或者是根本就没有映射, 那么这样肯定是又问题的. 所以需要驱动程序自己确保这些个问题得到妥善处理. 否则有可能引起程序崩溃.
 
那么一种一种来说, 首先我们先使用这个带缓冲区的来试试. 然后我们写个驱动程序, 模拟文件读写的过程. 利用带缓冲区的IO方式..
 
我说说下面这个代码的逻辑, 首先在Win32用户层这边先打开了驱动层的设备, 然后调用WriteFile写设备, 写了10次, 每次写完了再设置文件指针. 如果是调用Windows的文件操作函数的话, 写完了文件指针是会自己移动的, 这里我们的驱动进来写简单写. 就不做这个了. 还有一个调用SetFilePointer这号函数是不会向驱动发送IRP的, 只是修改了内核中某个数据结构的地址. 具体是什么, 我倒. 书上没说.
 
最后调用ReadFile一次读取出来.如果按照文件的读取方式的话, 应该把前面写入的一次全部读取出来才对, 那么事实上也是这样. 将前面写入的东西全部读取出来. 然后把他们显示出来, 基本上就是这些个东西. 这里我们使用的是带缓冲区的IO方式.. 

        http://www.joenchen.com/JoenTools/BufferIo.rar

这边是用户层的代码:

/*
	Windows内核下模拟文件的读写操作, IRP的处理(使用带缓冲区的IO方式) 3环代码
	编译方法参见makefile. TAB = 8
*/
#include <windows.h>
#include <stdio.h>

#define  SYS_LINK_NAME	"\\\\.\\SysLinkJoenDevice"
#define  MAX_LENGTH	1024

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

//===========================================================================
int Jmain() {
	HANDLE hFile = 0;
	BYTE byBuf[100];
	BYTE byBuf2[MAX_LENGTH];
	DWORD dwByteRead, i;

	//打开设备
	hFile = CreateFile( SYS_LINK_NAME, GENERIC_READ | GENERIC_WRITE, 0, 0,
			OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0  );

	if ( hFile == INVALID_HANDLE_VALUE ) {
		printf( "打开设备错误了!\n" );
		return -1;
	}else {
		printf( "打开设备成功!\n" );
	}

	__try {
		//循环多次向设备写入信息
		for( i = 0; i < 10; i++ ) {

			RtlFillMemory( byBuf, sizeof(byBuf ), i+'a' );
			if( !WriteFile( hFile,byBuf, sizeof(byBuf), &dwByteRead, NULL ) ) {
				printf( "写入失败!\n" );
				return -1;
			}else {
				printf( "写入成功!\n" );
			}

			//修改文件指针, 好像读取文件Windows内部会自动修改当前指针. 这里就手动了
			SetFilePointer(hFile, sizeof(byBuf), NULL, FILE_CURRENT );
		}

		//复位文件指针
		SetFilePointer(hFile,0, NULL, FILE_BEGIN );

		//向设备发送IRP_READ消息
		if( !ReadFile(hFile,&byBuf2, sizeof(byBuf2), &dwByteRead, NULL ) ) {
			printf( "读取文件失败!\n" );
		}else {
			printf( "读取文件成功!\n%s\n", byBuf2);
		}

	}__finally{
		if ( hFile ) {
			CloseHandle(hFile);
		}
		system( "pause" );
	}
	return 0;
}

这是内核态的代码:

/*
	Windows内核下模拟文件的读写操作, IRP的处理(使用带缓冲区的IO方式) 0环代码
	编译方法参见makefile. TAB = 8
*/
#include <ntddk.h>

#define  DEVICE_NAME	L"\\Device\\DevJoenDevice"
#define  SYS_LINK_NAME	L"\\??\\SysLinkJoenDevice"
#define  MAX_LENGTH	1024

typedef struct tagDeviceExt {
	ULONG		dwFileCurrentLength;
	CHAR*		pBuffer;
	PDEVICE_OBJECT	pDeviceObj;
	UNICODE_STRING	USzDeviceName;
	UNICODE_STRING	USzSysLinkName;
}DEVICEEXT, *PDEVICEEXT;
//===========================================================================
//驱动卸载例程
#pragma code_seg( "PAGE" )
VOID DriverUnLoad( PDRIVER_OBJECT pDriverObj ) {
	PDEVICEEXT pDeviceExt;
	PDEVICE_OBJECT pNextObj;

	pNextObj = pDriverObj->DeviceObject;
	while ( pNextObj != NULL ) {
		pDeviceExt = pNextObj->DeviceExtension;

		//删除符号链接, 和设备
		IoDeleteSymbolicLink( &pDeviceExt->USzSysLinkName );
		IoDeleteDevice( pDeviceExt->pDeviceObj );
		ExFreePool(pDeviceExt->pBuffer);

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

		pNextObj = pNextObj->NextDevice;
	}
}
//===========================================================================
//驱动派遣例程
NTSTATUS DispatchRoutin( PDEVICE_OBJECT pDeviceObj, PIRP pIrp ) {
	UCHAR Type;
	PIO_STACK_LOCATION Stack;
#if DBG
	//建立一个字符串数组与IRP类型对应起来
	static char* IrpName[] = {
		"IRP_MJ_CREATE",
		"IRP_MJ_CREATE_NAMED_PIPE",
		"IRP_MJ_CLOSE",
		"IRP_MJ_READ",
		"IRP_MJ_WRITE",
		"IRP_MJ_QUERY_INFORMATION",
		"IRP_MJ_SET_INFORMATION",
		"IRP_MJ_QUERY_EA",
		"IRP_MJ_SET_EA",
		"IRP_MJ_FLUSH_BUFFERS",
		"IRP_MJ_QUERY_VOLUME_INFORMATION",
		"IRP_MJ_SET_VOLUME_INFORMATION",
		"IRP_MJ_DIRECTORY_CONTROL",
		"IRP_MJ_FILE_SYSTEM_CONTROL",
		"IRP_MJ_DEVICE_CONTROL",
		"IRP_MJ_INTERNAL_DEVICE_CONTROL",
		"IRP_MJ_SHUTDOWN",
		"IRP_MJ_LOCK_CONTROL",
		"IRP_MJ_CLEANUP",
		"IRP_MJ_CREATE_MAILSLOT",
		"IRP_MJ_QUERY_SECURITY",
		"IRP_MJ_SET_SECURITY",
		"IRP_MJ_POWER",
		"IRP_MJ_SYSTEM_CONTROL",
		"IRP_MJ_DEVICE_CHANGE",
		"IRP_MJ_QUERY_QUOTA",
		"IRP_MJ_SET_QUOTA",
		"IRP_MJ_PNP",};
#endif
	Stack = IoGetCurrentIrpStackLocation( pIrp );
	Type = Stack->MajorFunction;

#if	DBG
	if ( Type <= sizeof(IrpName)/sizeof(IrpName[0]) ) {
		KdPrint(( "\t%s\n", IrpName[Type] ));
	}
#endif

	//简单完成IRP
	pIrp->IoStatus.Status = STATUS_SUCCESS;
	pIrp->IoStatus.Information = 0;
	IoCompleteRequest( pIrp, IO_NO_INCREMENT );
	return STATUS_SUCCESS;
}

//===========================================================================
//读请求
NTSTATUS DispatchRead( PDEVICE_OBJECT pDeviceObj, PIRP pIrp ) {
	ULONG ulReadLength;
	ULONG ulReadOffset;
	NTSTATUS Status;
	PDEVICEEXT pDeviceExt;
	PIO_STACK_LOCATION Stack;

	Stack = IoGetCurrentIrpStackLocation( pIrp );
	pDeviceExt = pDeviceObj->DeviceExtension;

	//欲读取的长度, 偏移
	ulReadLength = Stack->Parameters.Read.Length;
	ulReadOffset = (ULONG)Stack->Parameters.Read.ByteOffset.QuadPart;

	//如果要读取的偏移和长度大于最大缓冲区长度, 返回失败
	if ( ulReadLength + ulReadOffset > MAX_LENGTH ) {
		Status = STATUS_FILE_INVALID;
		ulReadLength = 0;
	}else {
		RtlCopyMemory(pIrp->AssociatedIrp.SystemBuffer,
			pDeviceExt->pBuffer+ulReadOffset, ulReadLength );
		Status = STATUS_SUCCESS;
	}

	//简单的完成IRP请求
	pIrp->IoStatus.Status = Status;
	pIrp->IoStatus.Information = ulReadLength;
	IoCompleteRequest( pIrp, IO_NO_INCREMENT );

	KdPrint(( "读取请求处理完毕!\n" ));
	return STATUS_SUCCESS;
}
//===========================================================================
//写请求
NTSTATUS DispatchWrite( PDEVICE_OBJECT pDeviceObj, PIRP pIrp ) {
	ULONG			ulWriteLength;
	ULONG			ulWriteOffset;
	CHAR*			pData = NULL;
	NTSTATUS		Status;
	PDEVICEEXT		pDeviceExt;
	PIO_STACK_LOCATION	Stack;	

	Stack = IoGetCurrentIrpStackLocation(pIrp);
	pDeviceExt = pDeviceObj->DeviceExtension;

	//获取欲写入长度与偏移
	ulWriteLength = Stack->Parameters.Write.Length;
	ulWriteOffset = (ULONG)Stack->Parameters.Write.ByteOffset.QuadPart;
	pData = pIrp->AssociatedIrp.SystemBuffer;

	//如果要写入的长度达到了最大长度, 返回无效.
	if ( ulWriteOffset + ulWriteLength > MAX_LENGTH ) {
		Status = STATUS_FILE_INVALID;
		ulWriteLength = 0;
	}else {
		//将3环那边的数据存储在缓冲区中
		RtlCopyMemory(pDeviceExt->pBuffer+ulWriteOffset,
			pIrp->AssociatedIrp.SystemBuffer, ulWriteLength);

		//重新设置下新的文件长度, 这里尽量模仿打开文件操作, 但是我们自己
		//创建的设备没有SetFilePointer函数, 还是不给力啊!
		if ( ulWriteLength+ulWriteOffset > pDeviceExt->dwFileCurrentLength ){
			pDeviceExt->dwFileCurrentLength = ulWriteOffset+ulWriteLength;
		}

		Status = STATUS_SUCCESS;
	}

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

	KdPrint(( "写入请求处理完毕!\n" ));
	return Status;
}

//===========================================================================
#pragma  code_seg( "INIT" )
NTSTATUS DriverEntry( PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pUSzRegPath ) {
	ULONG i;
	NTSTATUS Status;
	PDEVICEEXT pDeviceExt;
	PDEVICE_OBJECT pDeviceObj;
	UNICODE_STRING USzDeviceName = RTL_CONSTANT_STRING( DEVICE_NAME );
	UNICODE_STRING USzSysLinkName = RTL_CONSTANT_STRING( SYS_LINK_NAME );

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

	pDeviceObj->Flags		|= DO_BUFFERED_IO;
	pDeviceExt			= (PDEVICEEXT)pDeviceObj->DeviceExtension;
	pDeviceExt->pDeviceObj		= pDeviceObj;
	pDeviceExt->USzDeviceName	= USzDeviceName;
	pDeviceExt->USzSysLinkName	= USzSysLinkName;
	pDeviceExt->dwFileCurrentLength	= 0;
	pDeviceExt->pBuffer		= (CHAR*)ExAllocatePool(PagedPool,MAX_LENGTH);
	RtlZeroMemory(pDeviceExt->pBuffer, MAX_LENGTH );

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

	for( i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++ ) {
		pDriverObj->MajorFunction[i] = &DispatchRoutin;
	}

	//读请求特殊处理
	pDriverObj->DriverUnload = DriverUnLoad;
	pDriverObj->MajorFunction[IRP_MJ_READ]	= &DispatchRead;
	pDriverObj->MajorFunction[IRP_MJ_WRITE]	= &DispatchWrite;

	KdPrint(( "驱动安装完成!\n" ));
	return STATUS_SUCCESS;

}

发表评论


Warning: Undefined variable $user_ID in /www/wwwroot/joenchen.com/wp-content/themes/agan/comments.php on line 66

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