驱动:IRP异步完成 StartIO - IoStartPacket 问题??

okmnji79513 2010-07-22 10:04:14
StartIO 例程,IRP完成顺序的问题:

代码 基本上是 《Windows驱动开发技术详解》 第9章 "StartIOTest" 的代码,我只是稍作改变。
我把我 测试的 代码+工具 打包上传到了 如下地址 http://download.csdn.net/source/2564085 ,大家可以下载,帮忙测试 ,或者 看下面我贴的代码。

我先用 DriverMonitor "Open Driver --> Start Driver",然后 Dbgview.exe 查看内核输出(我没用DriverMonitor直接看内核输出,是因为我不会在DriverMonitor里看到 毫秒),然后 ,运行测试程序 Test。

用户层 测试代码 (Test) :

#include <windows.h>
#include <stdio.h>
#include <process.h>

HANDLE g_hDevice=0;
HANDLE g_hEvent[5]={0};

UINT WINAPI Thread(LPVOID context)
{
//显示线程创建时的时间
SYSTEMTIME sys;
printf("Enter Thread --> ");
GetLocalTime( &sys );
printf( "%4d/%02d/%02d %02d:%02d:%02d.%03d 星期%1d\n",sys.wYear,sys.wMonth,sys.wDay,sys.wHour,sys.wMinute, sys.wSecond,sys.wMilliseconds,sys.wDayOfWeek);

//等待5秒
OVERLAPPED overlap={0};
overlap.hEvent = g_hEvent[(int)context];
UCHAR buffer[10];
ULONG ulRead;

BOOL bRead = ReadFile(g_hDevice,buffer,10,&ulRead,&overlap);

//可以试验取消例程
//CancelIo(*(PHANDLE)context);

WaitForSingleObject(overlap.hEvent,INFINITE);
printf("%d ! --> ",context);
//显示 线程结束时的时间
GetLocalTime( &sys );
printf( "%4d/%02d/%02d %02d:%02d:%02d.%03d 星期%1d\n",sys.wYear,sys.wMonth,sys.wDay,sys.wHour,sys.wMinute, sys.wSecond,sys.wMilliseconds,sys.wDayOfWeek);
return 0;
}

int main()
{
g_hDevice =
CreateFile("\\\\.\\HelloDDK",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,//此处设置FILE_FLAG_OVERLAPPED
NULL );

if (g_hDevice == INVALID_HANDLE_VALUE)
{
printf("Open Device failed!\n");
return 1;
}

g_hEvent[0]=CreateEvent(NULL,FALSE,FALSE,NULL);
g_hEvent[1]=CreateEvent(NULL,FALSE,FALSE,NULL);
g_hEvent[2]=CreateEvent(NULL,FALSE,FALSE,NULL);
g_hEvent[3]=CreateEvent(NULL,FALSE,FALSE,NULL);
g_hEvent[4]=CreateEvent(NULL,FALSE,FALSE,NULL);

HANDLE hThread[5];
hThread[0] = (HANDLE) _beginthreadex (NULL,0,Thread,(void*)0,0,NULL);
hThread[1] = (HANDLE) _beginthreadex (NULL,0,Thread,(void*)1,0,NULL);
hThread[2] = (HANDLE) _beginthreadex (NULL,0,Thread,(void*)2,0,NULL);
hThread[3] = (HANDLE) _beginthreadex (NULL,0,Thread,(void*)3,0,NULL);
hThread[4] = (HANDLE) _beginthreadex (NULL,0,Thread,(void*)4,0,NULL);

//主线程等待5个子线程结束
WaitForMultipleObjects(5,hThread,TRUE,INFINITE);

//创建IRP_MJ_CLEANUP IRP
CloseHandle(g_hDevice);

return 0;
}

得到结果:

http://hi.csdn.net/attachment/201007/22/72724_1279763120P86V.jpg
问题
可以看到:
hThread[0]--g_hEvent[0] 对应 IRP 指针 -2109584408 (简称 A0)
hThread[1]--g_hEvent[1] 对应 IRP 指针 -2110436752 (简称 A1)
hThread[2]--g_hEvent[2] 对应 IRP 指针 -2109875600 (简称 A2)
hThread[3]--g_hEvent[3] 对应 IRP 指针 -2110581536 (简称 A3)
hThread[4]--g_hEvent[4] 对应 IRP 指针 -2110581928 (简称 A4)

IoCompleteRequest 的顺序 应该是 A0-->A1-->A2-->A3-->A4

但为何 "Leave HelloDDKStart ???" 的顺序是 :A4-->A3-->A2-->A1-->A0 ????

到了 用户层 更不明白了:
g_hEvent 返回的顺序 ,居然是 g_hEvent[1] --> g_hEvent[2] --> g_hEvent[3] --> g_hEvent[0] --> g_hEvent[4] ,并且 g_hEvent[0] 和 g_hEvent[4] 是同时返回的,很不明白,求解释????
...全文
353 点赞 收藏 8
写回复
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
okmnji79513 2010-07-24
[Quote=引用 7 楼 cnzdgs 的回复:]
因为你处理I/O的时间较长,在处理期间又有其它线程发出请求,所以才有这样的结果。在实际应用中,如果应用程序需要请求有序完成,是不会这样用多线程并行发送请求的。
[/Quote]

OK 结贴
回复
cnzdgs 2010-07-23
因为你处理I/O的时间较长,在处理期间又有其它线程发出请求,所以才有这样的结果。在实际应用中,如果应用程序需要请求有序完成,是不会这样用多线程并行发送请求的。
回复
okmnji79513 2010-07-23
[Quote=引用 3 楼 cnzdgs 的回复:]
当第0次请求时调用IoStartPacket进入了HelloDDKStartIO,由于处理需要5秒时间,在这5秒内,其它4个请求均已到达并插入队列,直到所有请求都处理完毕后,IoStartPacket函数才返回,之后第0次请求才return,所以实际的完成顺序是1、2、3、4、0,4和0几乎同时完成[/Quote]
这种做法就是:第一个进入队列的,总是要到队列里全部完成后才能 完成??
回复
okmnji79513 2010-07-23
[Quote=引用 3 楼 cnzdgs 的回复:]
当第0次请求时调用IoStartPacket进入了HelloDDKStartIO,由于处理需要5秒时间,在这5秒内,其它4个请求均已到达并插入队列,直到所有请求都处理完毕后,IoStartPacket函数才返回,之后第0次请求才return,所以实际的完成顺序是1、2、3、4、0,4和0几乎同时完成[/Quote]
那如果 我的 Test 程序里创建了100个线程的话(hThread[0]-->hThread[99]),那是不是顺序就是:
" 1、2、3、...、97、98、0和99几乎同时 " ?? 那 g_hEvent[0] 就总是在最后 或 最后第二个 被激活?? 总感觉 比较别扭 ......
回复
5个线程的完成顺序为未知,线程分到的时间片,以及在多CPU中的CPU空闲状态都不同.
stdout为线程安全,不可能完全同时.
系统时间精度只有0.011秒(除非取高精时间戳,精度也只有小数点4位).在1/100秒内取时间都是相同的结果
回复
cnzdgs 2010-07-22
你在StartIO例程中调用IoStartNextPacket,从而形成了递归,因为你是在IoStartNextPacket之后输出“Leave HelloDDKStartIO”的,所以输出的顺序是从后向前的。
当第0次请求时调用IoStartPacket进入了HelloDDKStartIO,由于处理需要5秒时间,在这5秒内,其它4个请求均已到达并插入队列,直到所有请求都处理完毕后,IoStartPacket函数才返回,之后第0次请求才return,所以实际的完成顺序是1、2、3、4、0,4和0几乎同时完成。
在用户层,因为是多个线程中分别处理,每个处理器同一时刻只能运行一个线程,线程0和4几乎同时输出信息,哪个先输出都有可能。
回复
okmnji79513 2010-07-22
内核代码:

/************************************************************************
* 文件名称:Driver.cpp
* 作 者:张帆
* 完成日期:2007-11-1
*************************************************************************/

#include "Driver.h"


#pragma LOCKEDCODE
VOID
HelloDDKStartIO(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
KIRQL oldirql;
KdPrint(("Enter HelloDDKStartIO : %d\n",Irp));

//获取cancel自旋锁
IoAcquireCancelSpinLock(&oldirql);
if (Irp!=DeviceObject->CurrentIrp||Irp->Cancel)
{
//如果当前有正在处理的IRP,则简单的入队列,并直接返回
//入队列的工作由系统完成,在StartIO中不用负责
IoReleaseCancelSpinLock(oldirql);
KdPrint(("Leave HelloDDKStartIO : %d\n",Irp));
return;
}else
{
//由于正在处理该IRP,所以不允许调用取消例程
//因此将此IRP的取消例程设置为NULL
IoSetCancelRoutine(Irp,NULL);
IoReleaseCancelSpinLock(oldirql);
}

KEVENT event;
KeInitializeEvent(&event,NotificationEvent,FALSE);

//等3秒
LARGE_INTEGER timeout;
timeout.QuadPart = -5*1000*1000*10;

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

Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0; // no bytes xfered
IoCompleteRequest(Irp,IO_NO_INCREMENT);

//在队列中读取一个IRP,并进行StartIo
IoStartNextPacket(DeviceObject,TRUE);

KdPrint(("Leave HelloDDKStartIO : %d\n",Irp));
}

VOID
OnCancelIRP(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
KdPrint(("Enter CancelReadIRP : %d\n",Irp));

if (Irp==DeviceObject->CurrentIrp)
{
//表明当前正在改由StartIo处理
//但StartIo并没有获取cancel自旋锁之前
//这时候需要
KIRQL oldirql = Irp->CancelIrql;

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

IoStartNextPacket(DeviceObject,TRUE);

KeLowerIrql(oldirql);
}else
{
//从设备队列中将该IRP抽取出来
KeRemoveEntryDeviceQueue(&DeviceObject->DeviceQueue,&Irp->Tail.Overlay.DeviceQueueEntry);
//释放Cancel自旋锁
IoReleaseCancelSpinLock(Irp->CancelIrql);
}


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

KdPrint(("Leave CancelReadIRP : %d\n",Irp));
}

/************************************************************************
* 函数名称:DriverEntry
* 功能描述:初始化驱动程序,定位和申请硬件资源,创建内核对象
* 参数列表:
pDriverObject:从I/O管理器中传进来的驱动对象
pRegistryPath:驱动程序在注册表的中的路径
* 返回 值:返回初始化驱动状态
*************************************************************************/
#pragma INITCODE
extern "C" NTSTATUS DriverEntry (
IN PDRIVER_OBJECT pDriverObject,
IN PUNICODE_STRING pRegistryPath )
{
NTSTATUS status;
KdPrint(("Enter DriverEntry\n"));

//设置卸载函数
pDriverObject->DriverUnload = HelloDDKUnload;

//设置派遣函数
pDriverObject->MajorFunction[IRP_MJ_CREATE] = HelloDDKDispatchRoutin;
pDriverObject->MajorFunction[IRP_MJ_CLOSE] = HelloDDKDispatchRoutin;
pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloDDKDispatchRoutin;
pDriverObject->MajorFunction[IRP_MJ_READ] = HelloDDKRead;
pDriverObject->MajorFunction[IRP_MJ_CLEANUP] = HelloDDKDispatchRoutin;
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = HelloDDKDispatchRoutin;
pDriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = HelloDDKDispatchRoutin;
pDriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = HelloDDKDispatchRoutin;
pDriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = HelloDDKDispatchRoutin;

//设置StartIO例程
pDriverObject->DriverStartIo = HelloDDKStartIO;

//创建驱动设备对象
status = CreateDevice(pDriverObject);

KdPrint(("Leave DriverEntry\n"));
return status;
}

/************************************************************************
* 函数名称:CreateDevice
* 功能描述:初始化设备对象
* 参数列表:
pDriverObject:从I/O管理器中传进来的驱动对象
* 返回 值:返回初始化状态
*************************************************************************/
#pragma INITCODE
NTSTATUS CreateDevice (
IN PDRIVER_OBJECT pDriverObject)
{
NTSTATUS status;
PDEVICE_OBJECT pDevObj;
PDEVICE_EXTENSION pDevExt;

//创建设备名称
UNICODE_STRING devName;
RtlInitUnicodeString(&devName,L"\\Device\\MyDDKDevice");

//创建设备
status = IoCreateDevice( pDriverObject,
sizeof(DEVICE_EXTENSION),
&(UNICODE_STRING)devName,
FILE_DEVICE_UNKNOWN,
0, TRUE,
&pDevObj );
if (!NT_SUCCESS(status))
return status;

pDevObj->Flags |= DO_BUFFERED_IO;
pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
pDevExt->pDevice = pDevObj;
pDevExt->ustrDeviceName = devName;

//创建符号链接
UNICODE_STRING symLinkName;
RtlInitUnicodeString(&symLinkName,L"\\??\\HelloDDK");
pDevExt->ustrSymLinkName = symLinkName;
status = IoCreateSymbolicLink( &symLinkName,&devName );
if (!NT_SUCCESS(status))
{
IoDeleteDevice( pDevObj );
return status;
}
return STATUS_SUCCESS;
}

/************************************************************************
* 函数名称:HelloDDKUnload
* 功能描述:负责驱动程序的卸载操作
* 参数列表:
pDriverObject:驱动对象
* 返回 值:返回状态
*************************************************************************/
#pragma PAGEDCODE
VOID HelloDDKUnload (IN PDRIVER_OBJECT pDriverObject)
{
PDEVICE_OBJECT pNextObj;
KdPrint(("Enter DriverUnload\n"));
pNextObj = pDriverObject->DeviceObject;
while (pNextObj != NULL)
{
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)
pNextObj->DeviceExtension;

//删除符号链接
UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName;
IoDeleteSymbolicLink(&pLinkName);

pNextObj = pNextObj->NextDevice;
IoDeleteDevice( pDevExt->pDevice );
}
}

/************************************************************************
* 函数名称:HelloDDKDispatchRoutin
* 功能描述:对读IRP进行处理
* 参数列表:
pDevObj:功能设备对象
pIrp:从IO请求包
* 返回 值:返回状态
*************************************************************************/
#pragma PAGEDCODE
NTSTATUS HelloDDKDispatchRoutin(IN PDEVICE_OBJECT pDevObj,
IN PIRP pIrp)
{
KdPrint(("Enter HelloDDKDispatchRoutin : %d\n",pIrp));

PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
//建立一个字符串数组与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",
};

UCHAR type = stack->MajorFunction;
if (type >= arraysize(irpname))
KdPrint((" - Unknown IRP, major type %X\n", type));
else
KdPrint(("\t%s\n", irpname[type]));


//对一般IRP的简单操作,后面会介绍对IRP更复杂的操作
NTSTATUS status = STATUS_SUCCESS;
// 完成IRP
pIrp->IoStatus.Status = status;
pIrp->IoStatus.Information = 0; // bytes xfered
IoCompleteRequest( pIrp, IO_NO_INCREMENT );

KdPrint(("Leave HelloDDKDispatchRoutin : %d\n",pIrp));

return status;
}

NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
IN PIRP pIrp)
{
KdPrint(("Enter HelloDDKRead : %d\n",pIrp));

PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)
pDevObj->DeviceExtension;

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

//将IRP插入系统的队列
IoStartPacket(pDevObj,pIrp,0,OnCancelIRP);

KdPrint(("Leave HelloDDKRead : %d\n",pIrp));

//返回pending状态
return STATUS_PENDING;
}

回复
okmnji79513 2010-07-22
UP 一下
回复
发动态
发帖子
硬件/系统
创建于2007-09-28

2590

社区成员

VC/MFC 硬件/系统
申请成为版主
社区公告
暂无公告