程序位置: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; }
【同步阻塞】调用,就像套接字的默认函数方式,如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; }
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; }