以前写过一篇Pnp管理器的文章,感觉写的有点肤浅,因此打算另起一篇写Pnp管理器。Pnp管理器是个大的组件:从内核到用户态都有涉及,因此需要分若干篇章分析。本篇立足于Pnp管理器中连接各个模块之间消息流。
系统引导过程中会初始化IO管理器,由IO管理器调用PnpInit对Pnp管理器进行初始化。跟Pnp消息相关的有两处:
NTSTATUS INIT_FUNCTION
IopInitPlugPlayEvents(VOID)
{
InitializeListHead(&IopPnpEventQueueHead);
KeInitializeEvent(&IopPnpNotifyEvent,
SynchronizationEvent,
FALSE);
return STATUS_SUCCESS;
}
初始化Pnp消息队列和用于同步Pnp管理器间各个模块的事件。从这段代码可以大致看出Pnp管理器属于生产者-消费者模型:内核把设备相关的动态信息加入到IopPnpEventQueueHead队列中,然后通知其他模块取走消息并处理。
内核中用树结构来管理设备,在PnpInit中IopRootDriverObject驱动创建一个Pdo作为整个设备树的根对象,当有新设备加入时,就加到连在根设备对象各个总线下,同时产生通知消息。根设备也是设备,所以在PnpInit中,IopRootDriverObject驱动创建Pdo的同时也向才创建不久的IopPnpEventQueueHead队列插入一条消息(第一条消息),通知内核有新设备加入,当然,现在还没组件处理这些消息。
IopQueueTargetDeviceEvent(&GUID_DEVICE_ARRIVAL,
&IopRootDeviceNode->InstancePath);
随着初始化进程的推进,IO管理器最终会创建一个内核线程,周而复始的处理IopPnpEventQueueHead中排队的消息:
hThread = CreateThread(NULL,
0,
PnpEventThread,
NULL,
0,
&dwThreadId);
static DWORD WINAPI
PnpEventThread(LPVOID lpParameter)
{
for (;;)
{
/* Wait for the next pnp event */
//这是个等待操作,等待PnpEvent有信号
Status = NtGetPlugPlayEvent(0, 0, PnpEvent, PnpEventSize);
if (UuidEqual(&PnpEvent->EventGuid, (UUID*)&GUID_DEVICE_ARRIVAL, &RpcStatus))
{
InterlockedPushEntrySList(&DeviceInstallListHead, &Params->ListEntry);
#else
InsertTailList(&DeviceInstallListHead, &Params->ListEntry);
#endif
SetEvent(hDeviceInstallListNotEmpty);
}
}
NtGetPlugPlayEvent函数是个无限等待函数,直到IopPnpNotifyEvent有事件,才从IopPnpEventQueueHead队列中取出一个元素。Pnp管理器的目的是为设备对象加载驱动,因此当线程从阻塞中返回到PnpEventThread中,PnpEventThread就获得设备信息,然后通知Pnp管理器其他组件部分准备安装驱动
谁会触发IopPnpNotifyEvent事件?搜索对IopPnpNotifyEvent的调用会发现IopActionInterrogateDeviceStack函数会间接触发IopPnpNotifyEvent事件。
NTSTATUS
IopActionInterrogateDeviceStack(PDEVICE_NODE DeviceNode,
PVOID Context)
{
/* Report the device to the user-mode pnp manager */
IopQueueTargetDeviceEvent(&GUID_DEVICE_ARRIVAL,
&DeviceNode->InstancePath);
}
每当总线设备枚举到到有新设备加入系统就会IoSynchronousInvalidateDeviceRelations,通知Pnp管理器原来的设备树发生变动,期间调用IopActionInterrogateDeviceStack使得总线上已有的设备重新获得设备信息(DeviceID,Resource等,这像是一个社区人员发生了变动,要通知社区中所有社员一样),变更设备信息后调用IopQueueTargetDeviceEvent准备为设备加载驱动~