程序位置:http://download.csdn.net/detail/dijkstar/8568463
StartIO非常好用,本质上,它把应用层传递进来的读请求、写请求、IOCTL请求放在一个队列里,由WDM内部串行调度,免去了自己去维护这样一个请求队列的复杂麻烦,下面是驱动层里StartIO的内容(特别处理IRP_MJ_READ请求):
#pragma LOCKEDCODE
VOID HelloDDKStartIO(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)
{
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
KIRQL oldirql;
//KdPrint(("Enter HelloDDKStartIO\n"));
//获取cancel自旋锁
IoAcquireCancelSpinLock(&oldirql);
if (Irp!=DeviceObject->CurrentIrp||Irp->Cancel)
{
//如果当前有正在处理的IRP,则简单的入队列,并直接返回
//入队列的工作由系统完成,在StartIO中不用负责
IoReleaseCancelSpinLock(oldirql);
KdPrint(("Leave HelloDDKStartIO\n"));
return;
}else
{
//由于正在处理该IRP,所以不允许调用取消例程
//因此将此IRP的取消例程设置为NULL
IoSetCancelRoutine(Irp,NULL);
IoReleaseCancelSpinLock(oldirql);
}
switch(IrpStack->MajorFunction)
{
case IRP_MJ_READ:
{
unsigned char i=0;
KEVENT event;
static unsigned char k = 0x30;//字符'0'开始
KeInitializeEvent(&event,NotificationEvent,FALSE);
//等2秒
LARGE_INTEGER timeout;
timeout.QuadPart = -2*1000*1000*10;
//定义一个3秒的延时,主要是为了模拟该IRP操作需要大概2秒左右时间
KeWaitForSingleObject(&event,Executive,KernelMode,FALSE,&timeout);
//模拟阻塞完毕后,向用户缓冲区拷贝点内容
ULONG uLen = IrpStack->Parameters.Read.Length;
k++;
for(i=0; i<uLen; i++)
{
unsigned char tmp= i+k;//0x30, 0x31,....开始拷贝,每次变化
unsigned char *p=(unsigned char *)Irp->AssociatedIrp.SystemBuffer;
memcpy(&p[i], &tmp, 1);
}
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = uLen; //
IoCompleteRequest(Irp,IO_NO_INCREMENT);
break;
}
case IRP_MJ_WRITE:
//写操作,立刻返回
//..............
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0; //
IoCompleteRequest(Irp,IO_NO_INCREMENT);
break;
case IRP_MJ_DEVICE_CONTROL:
//..............
//IOCTL操作,立刻返回
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0; //
IoCompleteRequest(Irp,IO_NO_INCREMENT);
break;
}
//在队列中读取一个IRP,并进行StartIo
IoStartNextPacket(DeviceObject,TRUE);
//KdPrint(("Leave HelloDDKStartIO\n"));
}
为了实现StartIO,拿“IRP_MJ_READ”请求来说,在该派遣处理中,首先要挂起(PENDING)这个读请求,然后将该请求插入系统队列,最后返回一个挂起状态(STATUS_PENDING):
NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp)
{
//KdPrint(("Enter HelloDDKRead\n"));
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)
pDevObj->DeviceExtension;
//将IRP设置为挂起
IoMarkIrpPending(pIrp);
//将IRP插入系统的队列
IoStartPacket(pDevObj,pIrp,0,OnCancelIRP);
//KdPrint(("Leave HelloDDKRead\n"));
//返回pending状态
return STATUS_PENDING;
}
这样,应用层对应的ReadFile()处理,有两种调用选择,一种是【同步阻塞】调用,一种是【异步立刻返回】调用。
【同步阻塞】调用,就像套接字的默认函数方式,如recvfrom,调用它时,直到它完成,才返回。要达到该目的,在CreateFile时,必须倒数第二参数【不能】带有FILE_FLAG_OVERLAPPED标志;这样的调用方式最简单,一般需要自己在应用层开启一个线程(CreateThread),在线程的while循环里不断的ReadFile(),因为是阻塞调用,该循环不消耗CPU;
【异步立刻返回】调用,调用ReadFile()时,立刻返回,要达到该目的,CreateFile时,必须倒数第二参数带有FILE_FLAG_OVERLAPPED标志;因为立刻返回,所以要自己创建等待事件实现阻塞(用GetOverlappedResult、WaitForMultipleObjects),下面是应用层异步/同步调用实现,先注释的是异步方式,后面注释打开的是同步方式:
typedef struct
{
HANDLE hDevice;
int inx;
}MY_TYPE;
UINT WINAPI Thread(LPVOID context)
{
MY_TYPE *p = (MY_TYPE *)context;
printf("进入线程: %d\n", p->inx);
DWORD dwStart = GetTickCount();
//等待2秒
OVERLAPPED overlap={0};
overlap.hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
UCHAR buffer[100] = {0};
ULONG ulRead;
/*
//
//下面为【异步立刻返回】操作
// CreateFile时,必须倒数第二参数带有FILE_FLAG_OVERLAPPED!!
//
BOOL bRead = ReadFile(p->hDevice, buffer, 10, &ulRead,&overlap);
if(bRead == FALSE)
{
DWORD nRet = GetLastError();
if( nRet== ERROR_IO_PENDING)//ERROR_IO_PENDING值=997
{
//阻塞方式1:推荐使用使用GetOverlappedResult,因为该函数能返回读到的个数
GetOverlappedResult(p->hDevice, &overlap, &ulRead, TRUE);
buffer[ulRead] = 0x00;
//阻塞方式2:
//WaitForMultipleObjects(overlap.hEvent, INFINITE);
DWORD dwEnd = GetTickCount();
printf("离开线程: %d, 耗时:%.1fs, 返回内容:%d, %s\n", p->inx, (dwEnd- dwStart)/1000.0, ulRead, buffer);
return 0;
}
else
printf("返回真正的错误: %d\n", nRet);
}
*/
//
// 下面为【同步阻塞】操作
// CreateFile时,必须倒数第二参数【不能】带有FILE_FLAG_OVERLAPPED!!
//
BOOL bRead = ReadFile(p->hDevice, buffer,10,&ulRead, NULL);
buffer[ulRead] = 0x00;
DWORD dwEnd = GetTickCount();
printf("离开线程: %d, 耗时:%.1fs, 返回内容:%d, %s\n", p->inx, (dwEnd- dwStart)/1000.0, ulRead, buffer);
return 0;
}
应用层启动程序:(特别注意CreateFile时的倒数第二参数的标志设置)
int main()
{
HANDLE hDevice =
CreateFile("\\\\.\\HelloDDK",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
//FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,//【异步立刻返回】
FILE_ATTRIBUTE_NORMAL,//【同步阻塞】
NULL );
if (hDevice == INVALID_HANDLE_VALUE)
{
printf("Open Device failed!");
return 1;
}
MY_TYPE my[3] = {0};
HANDLE hThread[3];
my[0].hDevice = hDevice;
my[0].inx = 0;
hThread[0] = (HANDLE) _beginthreadex (NULL,0,Thread,&my[0],0,NULL);
my[1].hDevice = hDevice;
my[1].inx = 1;
hThread[1] = (HANDLE) _beginthreadex (NULL,0,Thread,&my[1],0,NULL);
my[2].hDevice = hDevice;
my[2].inx = 2;
hThread[2] = (HANDLE) _beginthreadex (NULL,0,Thread,&my[2],0,NULL);
//主线程等待子线程结束
WaitForMultipleObjects(3,hThread,TRUE,INFINITE);
//创建IRP_MJ_CLEANUP IRP
CloseHandle(hDevice);
return 0;
}