天天看点

Pnp管理器(1)

    以前写过一篇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准备为设备加载驱动~

继续阅读