PNP管理器简析--基于ReactOS0.33
CSDN上转悠了一圈发现关于PNP管理的文章不多。那就由我献个丑,记录自己对PNP管理器的看法。
pnp管理器被描写叙述为向内核和应用程序提供关于设备拔插的通知,凭感觉,pnp管理器应该是个线程函数等待设备通知。搜索ReactOS发现有这么个函数符合这个功能:
static DWORD WINAPI
PnpEventThread(LPVOID lpParameter)
{
PPLUGPLAY_EVENT_BLOCK PnpEvent;
...
PnpEvent = HeapAlloc(GetProcessHeap(), 0, PnpEventSize);
...
for (;;)
{
DPRINT("Calling NtGetPlugPlayEvent()\n"); /* Wait for the next pnp event */
//这是个等待操作,等待PnpEvent有信号
Status = NtGetPlugPlayEvent(0, 0, PnpEvent, PnpEventSize);
....
NtPlugPlayControl(PlugPlayControlUserResponse, NULL, 0);
...
}
HeapFree(GetProcessHeap(), 0, PnpEvent); return ERROR_SUCCESS;
} /*
V操作 等待IopPnpNotifyEvent有信号 然后从IopPnpEventQueueHead队列中取pnp事件
*/
NTSTATUS STDCALL
NtGetPlugPlayEvent(IN ULONG Reserved1,
IN ULONG Reserved2,
OUT PPLUGPLAY_EVENT_BLOCK Buffer,
IN ULONG BufferSize)
{
PPNP_EVENT_ENTRY Entry;
NTSTATUS Status;
...
Status = KeWaitForSingleObject(&IopPnpNotifyEvent,
UserRequest,
KernelMode,
FALSE,
NULL);
/* Get entry from the tail of the queue */
Entry = CONTAINING_RECORD(IopPnpEventQueueHead.Blink,
PNP_EVENT_ENTRY,
ListEntry);
memcpy(Buffer,
&Entry->Event,
Entry->Event.TotalSize); return STATUS_SUCCESS;
} NTSTATUS STDCALL
NtPlugPlayControl(IN PLUGPLAY_CONTROL_CLASS PlugPlayControlClass,
IN OUT PVOID Buffer,
IN ULONG BufferLength)
{
...
switch (PlugPlayControlClass)
{
case PlugPlayControlUserResponse:
if (Buffer || BufferLength != 0)
return STATUS_INVALID_PARAMETER;
return IopRemovePlugPlayEvent(); case PlugPlayControlProperty:
if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_PROPERTY_DATA))
return STATUS_INVALID_PARAMETER;
return IopGetDeviceProperty((PPLUGPLAY_CONTROL_PROPERTY_DATA)Buffer); case PlugPlayControlGetRelatedDevice:
if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_RELATED_DEVICE_DATA))
return STATUS_INVALID_PARAMETER;
return IopGetRelatedDevice((PPLUGPLAY_CONTROL_RELATED_DEVICE_DATA)Buffer); case PlugPlayControlDeviceStatus:
if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_STATUS_DATA))
return STATUS_INVALID_PARAMETER;
return IopDeviceStatus((PPLUGPLAY_CONTROL_STATUS_DATA)Buffer); case PlugPlayControlGetDeviceDepth:
if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_DEPTH_DATA))
return STATUS_INVALID_PARAMETER;
return IopGetDeviceDepth((PPLUGPLAY_CONTROL_DEPTH_DATA)Buffer); case PlugPlayControlResetDevice:
if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_RESET_DEVICE_DATA))
return STATUS_INVALID_PARAMETER;
return IopResetDevice((PPLUGPLAY_CONTROL_RESET_DEVICE_DATA)Buffer); default:
return STATUS_NOT_IMPLEMENTED;
} return STATUS_NOT_IMPLEMENTED;
}
段代码有点TCPserveraccept事件通知模型:等待事件发生,然后分类处理事件。NtGetPlugPlayEvent函数中用到了两个重要的变量:IopPnpNotifyEvent和IopPnpEventQueueHead。
内核将新的pnp事件插入IopPnpEventQueueHead队列。然后唤醒应用层线程,IopPnpNotifyEvent就是内核和用户态PV操作的信号量。
这两个变量在系统启动时被初始化:
BOOLEAN
INIT_FUNCTION
NTAPI
IoInitSystem(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
{
KeInitializeGuardedMutex(&PnpNotifyListLock);
...
InitializeListHead(&PnpNotifyListHead);
...
PnpInit();
...
} VOID INIT_FUNCTION
PnpInit(VOID)
{
/* Initialize PnP-Event notification support */
Status = IopInitPlugPlayEvents();
//这个函数够重要的 调用失败就直接BUGCHECK了
if (!NT_SUCCESS(Status))
{
CPRINT("IopInitPlugPlayEvents() failed\n");
KEBUGCHECKEX(PHASE1_INITIALIZATION_FAILED, Status, 0, 0, 0);
}
} /* GLOBALS *******************************************************************/ static LIST_ENTRY IopPnpEventQueueHead;
static KEVENT IopPnpNotifyEvent; /* FUNCTIONS *****************************************************************/
//上面提到的重要的函数的实现
NTSTATUS INIT_FUNCTION
IopInitPlugPlayEvents(VOID)
{
InitializeListHead(&IopPnpEventQueueHead); KeInitializeEvent(&IopPnpNotifyEvent,
SynchronizationEvent,
FALSE); return STATUS_SUCCESS;
}
到这。PNP管理器这个server模型的骨架部分已经清晰了,后面循着骨架找齐完整的(相对完整的)结构。
总有些项目希望得到这种功能:当插入设备的时候获得通知。
界面开发人员可能会这么做:加入一个WM_DEVICECHANGE事件的回调函数
BEGIN_MESSAGE_MAP(CDeviceMonitorDlg, CDialog)
//}}AFX_MSG_MAP
ON_WM_DEVICECHANGE()
END_MESSAGE_MAP()
这个WM_DEVICECHANGE消息可能由WIN发出。也可能由某些后台程序监測到设备拔插后发出。为了监測某个设备拔插事件,就得注冊回调函数。就像为了观察北极气候就建立一个北极观測站一样。总线驱动程序假设想捕获到相关设备插入拔出事件,能够在其IRP_MN_START_DEVICE结束处用IoRegisterPlugPlayNotification注冊一回调函数。这个设备拔插事件回调函数注冊/注销接口例如以下:
IoRegisterPlugPlayNotification/IoUnregisterPlugPlayNotificatio STDCALL
IoRegisterPlugPlayNotification(
IN IO_NOTIFICATION_EVENT_CATEGORY EventCategory,
IN ULONG EventCategoryFlags,
IN PVOID EventCategoryData OPTIONAL,
IN PDRIVER_OBJECT DriverObject,
IN PDRIVER_NOTIFICATION_CALLBACK_ROUTINE CallbackRoutine,
IN PVOID Context,
OUT PVOID *NotificationEntry)
{
/*
windows内核中注冊回调事件都有相似的模式:创建一个回调事件项,
然后把各种參数填入,最后把这个新建项挂入队列
*/
PPNP_NOTIFY_ENTRY Entry;
...
Entry = ExAllocatePoolWithTag(
NonPagedPool,
sizeof(PNP_NOTIFY_ENTRY),
TAG_PNP_NOTIFY);
...
Entry->PnpNotificationProc = CallbackRoutine;
Entry->EventCategory = EventCategory;
Entry->Context = Context;
...
KeAcquireGuardedMutex(&PnpNotifyListLock);
InsertHeadList(&PnpNotifyListHead,
&Entry->PnpNotifyList);
KeReleaseGuardedMutex(&PnpNotifyListLock);
return STATUS_SUCCESS;
}
当中的通知队列和通知相互排斥锁:PnpNotifyListHead/PnpNotifyListLock在前面IoInitSystem时已经被初始化。接着看下这个监測站怎么工作:
前面往回调事件队列中插入了回调函数。对应的。内核中有个遍历队列并调用回调函数的接口:
VOID
IopNotifyPlugPlayNotification(
IN PDEVICE_OBJECT DeviceObject,
IN IO_NOTIFICATION_EVENT_CATEGORY EventCategory,
IN LPCGUID Event,
IN PVOID EventCategoryData1,
IN PVOID EventCategoryData2)
{
PPNP_NOTIFY_ENTRY ChangeEntry;
PLIST_ENTRY ListEntry;
PVOID NotificationStructure;
...
ListEntry = PnpNotifyListHead.Flink;
while (ListEntry != &PnpNotifyListHead)
{
ChangeEntry = CONTAINING_RECORD(ListEntry, PNP_NOTIFY_ENTRY, PnpNotifyList);
CallCurrentEntry = FALSE; switch (EventCategory)
{
case EventCategoryDeviceInterfaceChange:
{
if (ChangeEntry->EventCategory == EventCategory
&& RtlCompareUnicodeString(&ChangeEntry->Guid, (PUNICODE_STRING)EventCategoryData1, FALSE) == 0)
{
CallCurrentEntry = TRUE;
}
break;
}
case EventCategoryHardwareProfileChange:
{
CallCurrentEntry = TRUE;
break;
}
case EventCategoryTargetDeviceChange:
{
if (ChangeEntry->FileObject == (PFILE_OBJECT)EventCategoryData1)
CallCurrentEntry = TRUE;
}
default:
{
break;
}
} /* Move to the next element now, as callback may unregister itself */
ListEntry = ListEntry->Flink;
//调用注入的回调函数
if (CallCurrentEntry)
{
/* Call entry into new allocated memory */ KeReleaseGuardedMutex(&PnpNotifyListLock);
(ChangeEntry->PnpNotificationProc)(
NotificationStructure,
ChangeEntry->Context);
KeAcquireGuardedMutex(&PnpNotifyListLock);
}
}
}
看下ReactOS中谁会调用这个函数:
---- IopNotifyPlugPlayNotification Matches (6 in 3 files) ----
Deviface.c (ntoskrnl\io\iomgr): IopNotifyPlugPlayNotification(
Io.h (ntoskrnl\include\internal):IopNotifyPlugPlayNotification(
Pnpnotify.c (ntoskrnl\io\pnpmgr):IopNotifyPlugPlayNotification(
Pnpnotify.c (ntoskrnl\io\pnpmgr): DPRINT1("IopNotifyPlugPlayNotification(): unknown EventCategory 0x%x UNIMPLEMENTED\n", EventCategory);
Pnpnotify.c (ntoskrnl\io\pnpmgr): DPRINT1("IopNotifyPlugPlayNotification(): unknown EventCategory 0x%x UNIMPLEMENTED\n", EventCategory);
---- IoSetDeviceInterfaceState Matches (12 in 6 files) ----
Deviface.c (ntoskrnl\io\iomgr): * @name IoSetDeviceInterfaceState
Deviface.c (ntoskrnl\io\iomgr):IoSetDeviceInterfaceState(IN PUNICODE_STRING SymbolicLinkName,
Deviface.c (ntoskrnl\io\iomgr): DPRINT("IoSetDeviceInterfaceState('%wZ', %d)\n", SymbolicLinkName, Enable);
Deviface.c (ntoskrnl\io\iomgr): DPRINT("IoSetDeviceInterfaceState() returning STATUS_INVALID_PARAMETER_1\n");
Fdo.c (drivers\serial\serenum): Status = IoSetDeviceInterfaceState(&DeviceExtension->SerenumInterfaceName, TRUE);
Fdo.c (drivers\serial\serenum): DPRINT("IoSetDeviceInterfaceState() failed with status 0x%08lx\n", Status);
Ntoskrnl.def (ntoskrnl):IoSetDeviceInterfaceState@8
Pdo.c (drivers\usb\usbhub): Status = IoSetDeviceInterfaceState(&DeviceExtension->SymbolicLinkName, TRUE);
Pdo.c (drivers\usb\usbhub): DPRINT("Usbhub: IoSetDeviceInterfaceState() failed with status 0x%08lx\n", Status);
Pnp.c (drivers\serial\serial): IoSetDeviceInterfaceState(&DeviceExtension->SerialInterfaceName, FALSE);
Pnp.c (drivers\serial\serial): IoSetDeviceInterfaceState(&DeviceExtension->SerialInterfaceName, TRUE);
Winddk.h (include\ddk):IoSetDeviceInterfaceState(
从这些输出来看:当用户拔插串口。usb时都会使IoRegisterDeviceInterface注冊的回调函数得到运行。
假设把通知机制比方血液循环系统,那么到这,PNP管理器的循环系统也建立起来了。接着往下看其它脏器的组织结构,pnp管理器中管理的主要对象:设备驱动。
整个windows的设备驱动都是从根设备对象出发。层层堆叠形成一颗树结构(尽管说legacy设备不形成堆叠。可是windows还是把他纳入到堆叠结构。仅仅是其堆叠的PDO是个虚拟的设备对象)。附带说一下,看ReactOS源代码时,总认为PNP管理器跟linux的sysfs概念有点接近,大家都用虚拟的驱动/设备形成一个自上至下的树结构。
既然根设备是整个系统中设备驱动的基石,他的出场一定比較靠前--至少要在系统创建其它设备之前已经有了吧?来看下传说中根设备出场函数:
//创建IopRootDriverObject,IopRootDeviceNode,PnpRootDeviceOBject,并建立三者关系
VOID INIT_FUNCTION
PnpInit(VOID)
{
...
Status = IopCreateDriver(NULL, PnpDriverInitializeEmpty, NULL, 0, 0, &IopRootDriverObject);
if (!NT_SUCCESS(Status))
{
CPRINT("IoCreateDriverObject() failed\n");
KEBUGCHECKEX(PHASE1_INITIALIZATION_FAILED, Status, 0, 0, 0);
} Status = IoCreateDevice(IopRootDriverObject, 0, NULL, FILE_DEVICE_CONTROLLER,
0, FALSE, &Pdo);
if (!NT_SUCCESS(Status))
{
CPRINT("IoCreateDevice() failed\n");
KEBUGCHECKEX(PHASE1_INITIALIZATION_FAILED, Status, 0, 0, 0);
} Status = IopCreateDeviceNode(NULL, Pdo, NULL, &IopRootDeviceNode);
if (!NT_SUCCESS(Status))
{
CPRINT("Insufficient resources\n");
KEBUGCHECKEX(PHASE1_INITIALIZATION_FAILED, Status, 0, 0, 0);
} if (!RtlCreateUnicodeString(&IopRootDeviceNode->InstancePath,
L"HTREE\\ROOT\\0"))
{
CPRINT("Failed to create the instance path!\n");
KEBUGCHECKEX(PHASE1_INITIALIZATION_FAILED, STATUS_NO_MEMORY, 0, 0, 0);
} /* Report the device to the user-mode pnp manager */
IopQueueTargetDeviceEvent(&GUID_DEVICE_ARRIVAL,
&IopRootDeviceNode->InstancePath);
PnpRootDriverEntry(IopRootDriverObject, NULL);
IopRootDeviceNode->PhysicalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
//竟然自动调用AddDevice,而不是通过内核调用IopInitializeDevice
IopRootDriverObject->DriverExtension->AddDevice(
IopRootDriverObject,
IopRootDeviceNode->PhysicalDeviceObject);
...
}
PnpInit调用了PnpRootDriver的驱动入口和AddDevice函数。这么看来PnpRootDriver也算得上是Pnp设备了。
跟进PnpRootDriver和AddDevice函数一瞧到底:
NTSTATUS NTAPI
PnpRootDriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath)
{
DPRINT("PnpRootDriverEntry(%p %wZ)\n", DriverObject, RegistryPath);
//内核载入驱动后在IopInitializeDevice中调用AddDevice
DriverObject->DriverExtension->AddDevice = PnpRootAddDevice; DriverObject->MajorFunction[IRP_MJ_PNP] = PnpRootPnpControl;
//DriverObject->MajorFunction[IRP_MJ_POWER] = PnpRootPowerControl; return STATUS_SUCCESS;
}
PnpRootDriver应该算是一个最简单的DriverEntry入口函数了。如毛德操书中所述,Pnp设备至少要提供PNP派遣函数,PnpRootDriver真的仅仅做了这么件事!
NTSTATUS
STDCALL
PnpRootAddDevice(
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject)
{
Status = IoCreateDevice(
DriverObject,
sizeof(PNPROOT_FDO_DEVICE_EXTENSION),
NULL,
FILE_DEVICE_BUS_EXTENDER,
FILE_DEVICE_SECURE_OPEN,
TRUE,
&PnpRootDeviceObject);
...
DeviceExtension = (PPNPROOT_FDO_DEVICE_EXTENSION)PnpRootDeviceObject->DeviceExtension;
RtlZeroMemory(DeviceExtension, sizeof(PNPROOT_FDO_DEVICE_EXTENSION)); InitializeListHead(&DeviceExtension->DeviceListHead);
DeviceExtension->DeviceListCount = 0;
KeInitializeGuardedMutex(&DeviceExtension->DeviceListLock); Status = IoAttachDeviceToDeviceStackSafe(
PnpRootDeviceObject,
PhysicalDeviceObject,
&DeviceExtension->Ldo);
}
内核中维持一颗已装载的设备驱动模块树。树根是一个虚拟节点IopRootDeviceNode,节点在PnpInit函数中创建。
这个树根下挂满了各类总线设备和Legacy设备。形成设备树中的节点是结构:
typedef struct _DEVICE_NODE
{
struct _DEVICE_NODE *Parent;
struct _DEVICE_NODE *NextSibling;
struct _DEVICE_NODE *Child;
...
PDEVICE_OBJECT PhysicalDeviceObject;
PCM_RESOURCE_LIST ResourceList;
...
}
对于根设备IopRootDeviceNode,从PnpInit中能够看到其PhysicalDeviceObject指向IopRootDriverObject创建的Pdo。这个重要的Pdo是个无名设备,默默无闻的提供堆叠功能,真是居功至伟!PnpInit函数完毕了DEVICE_NODE IopRootDeviceNode和DriverObject IopRootDriverObject的创建之后立即调用IopRootDriverObject的AddDevice函数完毕设备堆叠。
结合PnpRootAddDevice的代码,能够看到PnpRootDeviceObject的驱动对象是IopRootDriverObject,而且堆叠在PnpInit创建的无名设备对象Pdo上。至此三个重要的对象之间的关系已经确立:IopRootDeviceNode是系统设备堆叠的根,其物理设备是一个无名的设备,并被堆叠了一个以IopRootDriverObject为驱动对象的设备PnpRootDeviceObject。这三个变量算构成PNP管理器的神经中枢。特别是PnpRootDeviceObject,其自己定义设备扩展域DeviceExtension,维护了一个PNPROOT_DEVICE结构。为什么这是个特别的域?以下这段代码做出了解释:
/*
这个函数主要干了2件事:创建PNPROOT_DEVICE节点和DEVICE_OBJECT设备对象
由于这个函数仅仅可能因NtLoadDriver创建非pnp设备而间接调用。为了维持
pnp设备树。NtLoadDriver将创建DEVICE_NODE节点。并在PnpRootCreateDevice这个函数
中创建PNPROOT_DEVICE节点和DEVICE_OBJECT pdo对象。
看结构。PNPROOT_DEVICE更像是一个pnp描写叙述符,描写叙述了pdo对象的附加信息。
最基本的。由于创建的DEVICE_NODE挂在IopRootDeviceNode下,因此把这个新
创建的PNPROOT_DEVICE节点挂在IopRootDeviceNode的链表下,这是在表明pnp
根节点下第一层子节点的意思吗?
*/
NTSTATUS
PnpRootCreateDevice(
IN PUNICODE_STRING ServiceName,
IN PDEVICE_OBJECT *PhysicalDeviceObject)
{
...
DeviceExtension = PnpRootDeviceObject->DeviceExtension;
KeAcquireGuardedMutex(&DeviceExtension->DeviceListLock);
...
Device = ExAllocatePoolWithTag(PagedPool, sizeof(PNPROOT_DEVICE), TAG_PNP_ROOT);
...
RtlZeroMemory(Device, sizeof(PNPROOT_DEVICE));
...
/*
创建一个用于给legacy设备进行堆叠的pnp root根下第一层pdo对象,
尽管legacy不提供堆叠可是也被纳入pnp树的管理范畴,因此创建
这么个父设备对象。
从IoCreateDevice的參数PnpRootDeviceObject->DriverObject也能够看出创建的pdo
并非给NtLoadDriver用的,是根节点驱动的设备对象
*/
Status = IoCreateDevice(
PnpRootDeviceObject->DriverObject,
sizeof(PNPROOT_PDO_DEVICE_EXTENSION),
NULL,
FILE_DEVICE_CONTROLLER,
FILE_AUTOGENERATED_DEVICE_NAME,
FALSE,
&Device->Pdo);
/*
上面创建PnpRootDeviceObject设备对象。属于IopRootDriverObject驱动的设备对象
以下将全部属于IopRootDriverObject驱动的设备对象增加链表DeviceExtension->DeviceListHead中
*/
PdoDeviceExtension = (PPNPROOT_PDO_DEVICE_EXTENSION)Device->Pdo->DeviceExtension;
RtlZeroMemory(PdoDeviceExtension, sizeof(PNPROOT_PDO_DEVICE_EXTENSION));
PdoDeviceExtension->Common.IsFDO = FALSE;
PdoDeviceExtension->DeviceInfo = Device;
...
InsertTailList(
&DeviceExtension->DeviceListHead,
&Device->ListEntry);
DeviceExtension->DeviceListCount++; *PhysicalDeviceObject = Device->Pdo;
}
如我的凝视所诉,PnpRootDeviceObject!DeviceExtension!DeviceListHead用于存放全部根节点下的设备,设备枚举时通过这个列表遍历各个设备。本以为通过NtLoadDriver装载驱动时会枚举设备请求资源,只是非常不幸。我没在ReactOS中找到相关的代码。仅仅在IoInitSystem时看到相关代码。
IoInitSystem调用IoSynchronousInvalidateDeviceRelations,这个函数名字真长按字面意思理解是"同步无效的设备关系"。怎么同步?
VOID
NTAPI
IoSynchronousInvalidateDeviceRelations(
IN PDEVICE_OBJECT DeviceObject,
IN DEVICE_RELATION_TYPE Type)
{
...
Status = IopInitiatePnpIrp(
DeviceObject,
&IoStatusBlock,
IRP_MN_QUERY_DEVICE_RELATIONS,
&Stack);
if (!NT_SUCCESS(Status))
{
DPRINT("IopInitiatePnpIrp() failed with status 0x%08lx\n", Status);
return;
} DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information;
...
}
向DeviceObject发送主功能号为PNP次功能号为IRP_MN_QUERY_DEVICE_RELATIONS的IRP请求,请求结果存放在IoStatusBlock.Information中。这里的DeviceObject即为IopRootDeviceNode->PhysicalDeviceObject,这个设备对象相应的PNP回调函数为:
PnpRootFdoPnpControl!FdoQueryDeviceRelations!PnpRootQueryDeviceRelations
这个函数调用EnumerateDevices,枚举注冊表Root下的子健。每一个子健代表一个设备项(应该是安装驱动时由inf文件设置),对于每一个被发现的子健创建PNPROOT_DEVICE结构的节点并挂入IopRootDevice扩展部的DeviceListHead队列中。当EnumerateDevices返回。为枚举到的每一个设备创建驱动对象和设备对象,这样整个设备对象树逐渐变得枝繁叶茂起来。
到这里,整个PNP差点儿相同完整了。可是还差一步,设备毕竟执行须要中断,port等资源来配合完毕。因此枚举设备后执行设备前必定要满足设备资源请求。这个在IopActionInterrogateDeviceStack函数中完毕。
NTSTATUS
IopActionInterrogateDeviceStack(PDEVICE_NODE DeviceNode,
PVOID Context)
{
...
Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
&IoStatusBlock,
IRP_MN_QUERY_ID,
&Stack);
...
Status = IopQueryDeviceCapabilities(DeviceNode, &DeviceCapabilities);
...
Stack.Parameters.QueryId.IdType = BusQueryInstanceID;
Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
&IoStatusBlock,
IRP_MN_QUERY_ID,
&Stack);
...
Status = IopInitiatePnpIrp(
DeviceNode->PhysicalDeviceObject,
&IoStatusBlock,
IRP_MN_QUERY_BUS_INFORMATION,
NULL);
...
//请求资源
Status = IopInitiatePnpIrp(
DeviceNode->PhysicalDeviceObject,
&IoStatusBlock,
IRP_MN_QUERY_RESOURCE_REQUIREMENTS,
NULL);
}
对于请求的资源存放在一个资源列表中:
DeviceNode->ResourceRequirements =
(PIO_RESOURCE_REQUIREMENTS_LIST)IoStatusBlock.Information;
然后由IopAssignDeviceResources和IopTranslateDeviceResources遍历资源队列。并调用对应的HAL函数分配物理资源
PNP管理器简析--基于ReactOS0.33的更多相关文章
- Linux内存管理机制简析
Linux内存管理机制简析 本文对Linux内存管理机制做一个简单的分析,试图让你快速理解Linux一些内存管理的概念并有效的利用一些管理方法. NUMA Linux 2.6开始支持NUMA( Non ...
- Spring系列.事务管理原理简析
Spring的事务管理功能能让我们非常简单地进行事务管理.只需要进行简单的两步配置即可: step1:开启事务管理功能 @Configuration //@EnableTransactionManag ...
- 光源 材质 简析——基于《real time rendering》第三版 第五章
对于真是世界的渲染,有三个重要的组成部分,光源,材质,以及摄像机.下面,我们一个一个来简单介绍一下. 光源:方向光,点光源,聚光灯.但是,在和物体表面交互的时候,光源对物体表面的影响是依赖光的辐照度( ...
- JavaScript包管理器综述
JavaScript包管理器综述 作者:chszs,未经博主同意不得转载.经许可的转载需注明作者和博客主页:http://blog.csdn.net/chszs 对于JavaScript来说.包管理器 ...
- Python核心技术与实战——二一|巧用上下文管理器和with语句精简代码
我们在Python中对于with的语句应该是不陌生的,特别是在文件的输入输出操作中,那在具体的使用过程中,是有什么引伸的含义呢?与之密切相关的上下文管理器(context manager)又是什么呢? ...
- Python 的上下文管理器是怎么设计的?
花下猫语:最近,我在看 Python 3.10 版本的更新内容时,发现有一个关于上下文管理器的小更新,然后,突然发现上下文管理器的设计 PEP 竟然还没人翻译过!于是,我断断续续花了两周时间,终于把这 ...
- 基于Web的Kafka管理器工具之Kafka-manager启动时出现Exception in thread "main" java.lang.UnsupportedClassVersionError错误解决办法(图文详解)
不多说,直接上干货! 前期博客 基于Web的Kafka管理器工具之Kafka-manager的编译部署详细安装 (支持kafka0.8.0.9和0.10以后版本)(图文详解) 问题详情 我在Kaf ...
- 基于IdentityServer4的OIDC实现单点登录(SSO)原理简析
写着前面 IdentityServer4的学习断断续续,兜兜转转,走了不少弯路,也花了不少时间.可能是因为没有阅读源码,也没有特别系统的学习资料,相关文章很多园子里的大佬都有涉及,有系列文章,比如: ...
- 0002 - Spring MVC 拦截器源码简析:拦截器加载与执行
1.概述 Spring MVC中的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),它主要用于拦截用户请求并作相应的处理.例如通过拦截器可以进行权限验证.记录请求信息的日 ...
随机推荐
- Opencv利用Mat访问像素值
如果是采用Mat形式存储,想要访问灰度图像的灰度值,可以采用如下方法: 如果是彩色图像,采用如下方法: 说明: 其中gray_value中存放灰度值,image是读入的图像,i表示行,j表示列: co ...
- 基于opencv的摄像头的标定
四个坐标系分别为:世界坐标系(Ow),摄像机坐标系(Oc),图像物理坐标系(O1,单位mm),图像像素坐标系(O,位于视野平面的左上角,单位pix). 空间某点P到其像点p的坐标转换过程主要是通过这四 ...
- poj2253 最短路变形
题目连接:http://poj.org/problem?id=2253 Description Freddy Frog is sitting on a stone in the middle of a ...
- HDU 5935 Car【贪心,枚举,精度】
Problem Description Ruins is driving a car to participating in a programming contest. As on a very t ...
- Java的位运算符与二进制转换
转换: Java整型数据类型有:byte.char.short.int.long.要把它们转换成二进制的原码形式,必须明白他们各占几个字节.,一个字节==8位数 数据类型 ...
- rsync用于同步目录
rsync是unix/linux下同步文件的一个高效算法,它能同步更新两处计算机的文件与目录,并适当利用查找文件中的不同块以减少数据传输.rsync中一项与其他大部分类似程序或协定中所未见的重要特性是 ...
- 01、Mecanim动画系统
序言:Mecanim动画系统是Unity4.0之后退出的新版动画系统,非常适合人类动画系统.本文是作为自己的学习来讲解的, 可能会有些啰嗦,但尽量把自己的坑都为大家列出来,让大家理解透彻. 一.文件的 ...
- 转 select()函数以及FD_ZERO、FD_SET、FD_CLR、FD_ISSET
select函数用于在非阻塞中,当一个套接字或一组套接字有信号时通知你,系统提供select函数来实现多路复用输入/输出模型,原型:int select(int maxfd,fd_set *rdset ...
- POJ 2559 Largest Rectangle in a Histogram(单调栈)
[题目链接] http://poj.org/problem?id=2559 [题目大意] 给出一些宽度为1的长方形下段对其后横向排列得到的图形,现在给你他们的高度, 求里面包含的最大长方形的面积 [题 ...
- 【DFS】Gym - 101142C - CodeCoder vs TopForces
就按照题意建出有向图来(n个点,2n-2条边),然后从按随便一个rating排序,从最后一个开始dfs,用vis数组防止重复访问,因为每次之前的肯定能访问之后的(及之后的能访问的),所以不会有重复.就 ...