原来以为写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方式.. 

        BufferIo.rar

这边是用户层的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/*
	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;
}

这是内核态的代码:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
/*
	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;

}