IoRegisterPlugPlayNotification源码分析
  Wn1uLk9Db141 2023年11月02日 41 0


    IoRegisterPlugPlayNotification函数的作用是为指定guid的设备注册一个回调函数,当进入设备驱动并调用IoSetDeviceInterfaceState使能设备访问接口后,让系统调用之前注册的回调函数,以实现通知的目的.wrk1.2源码中并没有IO管理器的实现,因此,只能退而求其次,参考ReactOS中关于IoRegisterPlugPlayNotification的实现.

    ReactOS在系统初始化阶段调用PoInitSystem初始化电源管理器时,会以

IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange,
0, /* The registry has not been initialized yet */
(PVOID)&GUID_DEVICE_SYS_BUTTON,
IopRootDeviceNode->
PhysicalDeviceObject->DriverObject,
PopAddRemoveSysCapsCallback,
NULL,
&NotificationEntry);

的形式为集成在主板按键上的调用IoRegisterPlugPlayNotification.

    参数1 EventCategoryDeviceInterfaceChange表明Pnp管理器在接到设备接口状态变化(Enable/Disable)时调用先前注册的回调函数;

    参数3 GUID_DEVICE_SYS_BUTTON是指被IoSetDeviceInterfaceState操作的接口;

    参数5和6是回调函数的入口地址和参数;

    不得不说,系统中注册回调函数都有一定的相似性,步骤可归纳为2步:1).为一个Entry分配pool内存,并用回调函数入口地址和参数Context初始化Entry,并初始化其他域(如回调条件);2).将刚分配的Entry结构以互斥的方式加入到内核中相应的队列中.IoRegisterPlugPlayNotification也是如此:

{
//分配池内存
Entry = ExAllocatePoolWithTag(
NonPagedPool,
sizeof(PNP_NOTIFY_ENTRY),
TAG_PNP_NOTIFY);
//从interface获得设备的名字
Status = IoGetDeviceInterfaces(
(LPGUID)EventCategoryData,
NULL, /* PhysicalDeviceObject OPTIONAL */
0, /* Flags */
&SymbolicLinkList);
NotificationInfos.Version = 1;//以下是初始化Entry结构
NotificationInfos.Size = sizeof(DEVICE_INTERFACE_CHANGE_NOTIFICATION);
RtlCopyMemory(&NotificationInfos.Event, &GUID_DEVICE_INTERFACE_ARRIVAL, sizeof(GUID));
RtlCopyMemory(&NotificationInfos.InterfaceClassGuid, EventCategoryData, sizeof(GUID));


Entry->PnpNotificationProc = CallbackRoutine;
Entry->EventCategory = EventCategory;
Entry->Context = Context;
//最后,获得互斥锁,并插入PnpNotifyListHead
KeAcquireGuardedMutex(&PnpNotifyListLock);
InsertHeadList(&PnpNotifyListHead,
&Entry->PnpNotifyList);
KeReleaseGuardedMutex(&PnpNotifyListLock);
}

    回调函数注册后,将在什么情况下才会被调用?通过搜索对PnpNotifyListHead的引用,可以发现端倪:

Pnpnotify.c!IopNotifyPlugPlayNotification中遍历PnpNotifyListHead:
ListEntry = PnpNotifyListHead.Flink;
while (ListEntry != &PnpNotifyListHead)
{
ChangeEntry = CONTAINING_RECORD(ListEntry, PNP_NOTIFY_ENTRY,
PnpNotifyList);
ListEntry = ListEntry->Flink;
}

这是典型的遍历队列操作啊,有必要介绍一下IopNotifyPlugPlayNotification的流程:

一般IoSetDeviceInterfaceState会以下列方式调用IopNotifyPlugPlayNotification

EventGuid = Enable ? &GUID_DEVICE_INTERFACE_ARRIVAL : &GUID_DEVICE_INTERFACE_REMOVAL;
IopNotifyPlugPlayNotification(
PhysicalDeviceObject,
EventCategoryDeviceInterfaceChange,
EventGuid,
&GuidString,
(PVOID)SymbolicLinkName);

    当IopNotifyPlugPlayNotification被调用时,它搜索PnpNotifyListHead链表,逐项比对已注册在PnpNotifyListHead队列中的PNP_NOTIFY_ENTRY各项,如果PNP_NOTIFY_ENTRY中接口GUID和产生设备插拔事件的接口GUID相同,则调用PNP_NOTIFY_ENTRY中的回调函数,去完成一些挂起的IRP请求.


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  iD7FikcuyaVi   2023年11月30日   26   0   0 MacWindowsandroid
  9E2BTpjt8nym   2023年12月06日   35   0   0 WindowsgitCentOS
Wn1uLk9Db141