之前写过1篇Pnp管理器的文章,感觉写的有点浮浅,因此打算另起1篇写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驱动创建1个Pdo作为全部装备树的根对象,当有新装备加入时,就加到连在根装备对象各个总线下,同时产生通知消息。根装备也是装备,所以在PnpInit中,IopRootDriverObject驱动创建Pdo的同时也向才创建不久的IopPnpEventQueueHead队列插入1条消息(第1条消息),通知内核有新装备加入,固然,现在还没组件处理这些消息。
IopQueueTargetDeviceEvent(&GUID_DEVICE_ARRIVAL,
&IopRootDeviceNode->InstancePath);
随着初始化进程的推动,IO管理器终究会创建1个内核线程,循环往复的处理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队列中取出1个元素。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等,这像是1个社区人员产生了变动,要通知社区中所有社员1样),变更装备信息后调用IopQueueTargetDeviceEvent准备为装备加载驱动~