Interpreting the hierarchical Driver Model of Windows 2000/XP
WebSphere (http://webcrazy.yeah.net)
Scalability is one of the goals of the Windows NT/2000/XP design. Its Hierarchical driver model is the best embodiment of scalability. The implementation of layering depends on two important designs of the io manager: 1. Any driver in Windows is designed in the Client/Server mode. For a client driver, obtain the device object exported by the server driver through iogetdeviceobjectpointer and request the service from the server through iocalldriver of the IO manager. Iocalldriver actually calls the dispatch entry (callback function) of the server based on the client call parameters (through IRP) to accept the client request. 2. the IO manager implements a layered data structure, stores a relationship in the device_object object, and automatically sends the request IRP to the highest device in the device stack, which determines how to handle it, it can be processed by itself, or passed down to achieve the goal of layering. In view of this capability, the hierarchical driver model can implement many applications, such as file monitoring, encryption, anti-virus, etc. Due to the introduction of PNP, this application will be more extensive. In fact, this hierarchical model is everywhere in Windows NT/2000/XP. If you don't believe it, run the following command to see it:
Findstr/M ioattachdevice % SystemRoot %/system32/Drivers/*. sys
Almost all listed drivers can be seen as examples of hierarchical drivers. The introduction to the hierarchical driver is almost full of any book that introduces the Windows Driver. This article does not want to repeat these content much more. It aims to explain the implementation of this hierarchy from the implementation of the underlying data structure. Start with device_object. Some definitions of this structure are as follows:
Typedef struct declspec_align (memory_allocation_alignment) _ device_object {
.
.
.
Struct _ driver_object * driverobject;
Struct _ device_object * nextdevice;
Struct _ device_object * attacheddevice;
.
.
.
Pvoid deviceextension;
.
.
.
Cchar stacksize;
.
.
.
Struct _ DEFINE bj_extension * deviceobjectextension;
.
.
.
} Device_object;
The driverobject member is the driver_object corresponding to the deviceobject. Through this object, the IO manager can know how to provide services for this device (by calling the dispatch entry provided by driverobject ). A driver_object can provide services for at most one device_object. Each device serves different types of physical or logical devices. They may also play different roles, such as PDO and fdo introduced by PNP. I will introduce them in detail below. The reason is that the nextdevice member is used to link these device_objects. Therefore, we can draw a conclusion that, as described in the previous sentence, the relationship between PDO and fdo exported by the same driver is logically established through the nextdevice member. For the driver of legacy for attacheddevice members (before Windows NT 4.0, it can also be used normally in later versions ), this field is used to implement the second function provided by the IO manager mentioned at the beginning of this article. As shown in the following figure, attachedevice1 is appended to device1. In this case, attachdevice1 and device1 are generally not served by the same driver, that is, they are not linked together through the nextdevice members, in this way, we can write another driver and append a device to an existing device to change or monitor the behavior of this device. Of course, the attacheddevice member of device1 points to attachdevice1 at this time. The attacheddevice1 is not attached to any device, so its attacheddevice Member points to null. When you call iocalldriver to request the device1 service, the IO manager calls iogetattacheddevice to obtain the highest-level device attached to device1. Here is attacheddevice1, and for device2, It is attacheddevice3. If it is not attached to any device, it is of course device1. In this way, the attached device has the opportunity to perform corresponding operations. In addition, for device1 and device2, it is obvious that device1 is located on top of device2, and this is the first case we will introduce at the beginning. device1 requests device2 through iocalldriver, establish a logical relationship (we cannot identify this relationship through any data structure. This is usually the logical relationship designed by the driver developer. How can we know this relationship on Windows 2000/XP, of course, the best difference is the DDK document ).
| ------------- | ____ Attachdevice1
| Device1 |
| ------------- |
------ Attachdevice3
| ------------- | ____ Attachdevice2 --- |
| Device2 |
| ------------- |
The deviceextension member is usually a structure defined by the driver developer. It stores device-related content, such as the difference between PDO and fdo. When iocreatedevice is called, specify the size. The IO manager allocates the non-Paging memory of sizeof (device_object) + deviceextensionsize for the device object, so that the two structures are physically consecutive, therefore, we often see definitions like volume_device_object in some file system drivers (the standard device_object is followed by a dedicated definition for volume, avoid referencing this member at the dispatch entry every time ).
Stacksize refers to the number of devices on the current device stack (the number of attacheddevices plus the number of devices themselves). It is used by the IO manager to specify the number of stack_location when allocating IRPs.
All the above descriptions constitute the Windows layered driver model before Windows NT 4.0. This is also the main working idea of the legacy (legacy) layered driver. Attacheddevice indicates the device that has been attached. Microsoft introduced an attachedto in the deviceobjectextension structure member of Windows 2000 to indicate the low-level device that has been attached by the current device, which is not implemented in Windows NT. Deviceobjectextension is a very important structure. The adddevice entry that supports the pnp wdm driver is also introduced here. It is a structure defined by the system (different from deviceextension ). Driver_object also has a similar structure called driverextenion. The latter has a clientdriverextension structure, which is allocated by ioallocatedriverobjectextension and obtained by iogetdriverobjectextension. Classpnp. sys implements disk. sys, tape. sys and CDROM. sys management; NDIS. sys also uses this method to manage various miniport/IM/protocol drivers.
In Windows Firewall, several commands are exported with the drvo_builtin_driver (ntddk. the driver of the signature, which is defined in H. They are pnpmanager, wmixwdm, and acpi_hal, the latter is the Hal that supports ACPI (this is the situation on my Windows XP connector alicloud machine, I do not know what acpios are like, my machine ntoskrnl.exe also exports/filesystem/raw driver for file system support, I think the names of these devices may vary depending on the configurations of the random device and the Windows version. In fact, on my Windows 2000 Server sp0release, ntoskrnl.exe generates the following four devices: pnpmanager, pci_hal, WDM, and raw, my descriptions are basically on my desktop. I may also point out that some differences may not exist.) The first two drivers are introduced to support WDM. Wmixwdm exports wmidatadevice for WMI support, which has nothing to do with the hierarchical driver discussed in this article. I will focus on pnpmanager below.
Before introduction, let's take a look at the devmgmt. msc "Sort devices by connection" View:
Tsu00 (machine name, virtual root bus enumeration implemented by pnpmanager)
|
| + Advanced configuration and Power Interface (ACPI) PC (implementation of the acpi_hal driver in ntoskrnl)
|
| + Micrsoft ACPI-compliant system (enumerated by ACPI. sys)
|
| + PCI Bus (implemented by PCI. sys)
|
| + (Connected device)
Pnpmanageris a main driver (implemented in ntoskrnl.exe). If you have checked build's ntoskrnl.exe, you can easily find it from base/ntos/IO/pnpmgr/pnpdd. c implementation. Have you seen the Windows XP source tree exported by sysinternals ?), She implements a virtual bus called root. All legacy devices are connected to this virtual bus. If you do not believe it, choose show hidden devices on the sketch listed on devmgmt. MSC ". In this sense, she wants to create a PDO for every device connected to her. These PDO names are usually in hexadecimal format starting with 00000001. For example, in my experiment, the device names are from 1 to 00000050, A total of 80 PDO instances (not named in this way on my Windows 2000 server host, although it is also based on hexadecimal, but it starts from a very large value ). I like the devicetree of some OSR releases along with Windows xp ddk. But for better understanding, I 'd like to make an experiment with windbg:
Find the PDO implemented by pnpmanager attached to the above sketch acpi_hal. This is usually the first PDO implemented by pnpmanager, that is, 00000001 (if the device generated by your pnpmanager is not named like this, please use! Drvobj pnpmanager finds the generated PDO. In my Windows 2000 Server sp0 notebook, the first PDO of pnpmanager is used to serve the ESS sound card, instead of the pci_hal I originally thought, you may need another one! Devstack or what we will introduce below! Devnoe command, the method is not detailed ):
Kd>! Object/device/00000001
Object: 812b4410 type: (812b4048) Device
Objectheader: 812b43f8
Handlecount: 0 pointercount: 5
Directory object: e10011b0 name: 00000001
Kd>! Devobj 812b4410
Device object (812b4410) is:
00000001/driver/pnpmanager driverobject 812b4980
Current IRP 00000000 refcount 0 type 00000004 flags 00001040
DACL e1518a6c devext 812b44c8 jwbjext 812b44d0 devnode 812b42b8
Extensionflags (0000000000)
Attacheddevice (upper) 812f6bb0/driver/acpi_hal
Device queue is not busy.
We can easily find that acpi_hal is connected to device 00000001 by using a line of attacheddevice output above, which is consistent with what we see on devmgmt. MSC. In fact, attached to this PDO is a device called fdo that is served by acpi_hal. Both PDO and fdo are internally represented by device_object, as mentioned above, bus driver developers usually use a flag in deviceextension to distinguish between PDO and fdo devices of the same driver service. Fdo is usually established by the adddevice entry of the driver. After it is established, ioattachdevicetodevicestack is used to attach it to the PDO provided by the lower-layer bus driver, as we can see in the windbg above. The attached operation is the same as the legacy device discussed at the beginning. It is also a member of the attacheddevice that uses device_object.
You may be troubled by how the adddevice entry of pnpmanager is implemented. We know that the pnpmanager implementation is called the root bus driver. Since it is root, there is certainly no PDO provided by the attached. In fact, you can use windbg to check its implementation: In Free Build XP, it only implements return STATUS_SUCCESS (XOR eax, eax/RET. When I use checked build, it only checks IRQL: rtlassert (kegetcurrentirql () <= apc_level ).
Of course, for acpi_hal or PCI bus, the adddevice and pnpmanager implementations are definitely different. They must install the iocreatedevice into fdo, attached to the PDO provided by pnpmanager or the PDO provided by the upper-layer bus to implement hierarchical relationships. We continue to use windbg to verify my ideas like this:
Kd>! Drvobj/driver/acpi_hal
Driver object (812f6ce8) is:
/Driver/acpi_hal
Driver extension list: (ID, ADDR)
Device object list:
812f6a90 812f6bb0
Which of the two devices exported by acpi_hal are PDO and fdo? (You should understand that there is at least one fdo ). We know that after the PnP Manager discovers the bus, it first calls the adddevice entry of the bus driver, and then sends various minorfunctions of irp_mj_pnp to indicate the bus driver enumeration bus, create PDO for each device connected to the top. Here I will only describe the general situation. For virtual buses such as toaster attached to DDK, the devices on the enumerated bus send the corresponding IOCTL through the application to indicate the creation of PDO (enable the PnP Manager to send irp_mj_pnp through ioinvalidatedevicerelations ). Of course, even the virtual bus implemented by toaster and the adddevice entry, that is, fdo is always established before PDO. For devices of the same driver_object service, one-way linked list is formed through deviceobject of driver_object. This deviceobject indicates the linked list header, which is connected to the linked list by nextdevice of device_object. For devices created by iocreatedevice, the created devices always Insert the header, while fdo is basically the first one, so it is always at the end of the table. With these analyses, the output 812f6bb0 on acpi_hal is fdo, while the output 812f6a90 is PDO. OK. In this way, the PDO is the lower-layer PDO of the bus driver implemented by the upper-layer ACPI. sys.
Kd>! Devobj 812f6a90
Device object (812f6a90) is:
00000052/driver/acpi_hal driverobject 812f6ce8
Current IRP 00000000 refcount 0 type 0000002a flags 00001040
DACL e1518a6c devext 812f6b48 running bjext 812f6b60 devnode 812f63a8
Extensionflags (0000000000)
Attacheddevice (upper) 812ad960/driver/ACPI
Device queue is not busy.
Check whether the upper layer is/driver/ACPI (attacheddevice line ). The PCI bus is implemented on the Micrsoft ACPI-compliant system implemented by attached to ACPI. sys. Note that unlike acpi_hal, ACPI only has one PDO, And the PCI bus is not the first PDO. With this knowledge, I think you can easily find the PDO attached to the PCI bus. A simpler method is to use it! The devstack command dump the fdo of the PCI bus.
Kd>! Drvobj PCI
Driver object (812ef850) is:
/Driver/PCI
Driver extension list: (ID, ADDR)
Device object list:
812f39e8 812f3d58 812f4e40 812f4038
812f0710 812f0908 812f0c58 812f14e8
812f0e38
Kd>! Devstack 812f0e38
! Mongobj! Drvobj! Devext objectname
> 812f0e38/driver/PCI 812f0ef0
812dc8c0/driver/ACPI 812f5660 00000058
! Devnode 812f1008:
Deviceinst is "ACPI/pnp0a03/2 & daba3ff & 0"
Servicename is "PCI"
The devstack Command actually uses the attacheddevice of device_object and the attachedto in the deviceobjectextension (note that this is the deviceobjectextension instead of deviceextension) structure member to display the device stack. Of course, the devstack command also displays the device_node corresponding to the device. Device_node is a system data structure introduced to support PNP. The complete device_node definition is very complex and very large, and I will not list it, several important members, such as sibling (brother device_node), Child (sub-device_node), parent (parent device_node), device status pnp_devnode_state, resource usage cm_resource_list, interface type interface_type, device ID, service name servicename, and so on.
From my prompt that devicenode contains sibling, child, and parent, it is easy to think that the system may make all devicenode into a tree (similar to the directory tree of the file system ), in fact, the kernel variable ioprootdevicenode indicates the root of the tree .! The devnode command shows the root node. If you use it! Devnode 0 1 command, you will see a devmgmt. msc version of windbg. In fact, the system's setupdi (the API exported by setupapi. DLL for device installation) is used to dump all devices. Devmgmt. msc indirectly uses these APIs (unlike me, you can refer to the. msc file as an XML file parsed by MMC. EXE ). Similarly, devicetree of OSR must also use these Apis.
So far, you may be even more difficult. What is PDO? How does the system know which bus is attached to it to form a device layer. The secret lies in the registry. When a device is installed, it adds content to the Registry through the. inf file to indicate the loading sequence of the system. The earlier. inf file was really complicated, and in my opinion it was no less than a Perl script. Windows 2000 does too much automatic work for you (I really want to know what it is ).
In the registry, HKLM/system/CurrentControlSet/Enum and HKLM/system/CurrentControlSet/control/class work together to complete such a task, of course, like the legacy driver, HKLM/system/CurrentControlSet/services are indispensable. To completely describe the Windows 2000/XP hierarchical driver model, it is necessary to mention the filter driver at the end, which is a type of driver attached to the bus driver or another driver for enhancement, changes some features of the original device. Provided by the values of upperfilters and lowerfilters Of The Enum and class items in the mentioned registry.
Finally, it is explained that the hierarchical driver of Windows 2000/XP separates many concepts, such as the middle layer driver, the old name, such as the class driver that is everywhere in Windows 2000/XP. The class driver implements the common functions of a certain type of devices without the actual hardware access. For example, a disk driver contains disks in Windows 2000/XP. sys, tape. sys, CDROM. SYS. They all use classpnp. sys implements the class driver, with disk. sys, for example, is based on the underlying atapi, whether it is an IDE interface or a SCSI interface. sys or scsiport. sys these miniport/port drivers implement interaction with specific hardware.
In addition, it can be said that filesystem filter is a legacy hierarchical Driver (only the attacheddevice member of device_object is used. Network drivers, such as NDIS Intermediate Drivers (including NDIS filter intermediate drivers and ndis mux intermediate drivers), can also be seen as layered-driven applications, only NDIS wrapper Library (NDIS. sys) hides too much information (hides the true face of IRP), or NDIS. sys uses its internal structure definition, such as ndis_m_driver_block, ndis_miniport_block, ndis_protocol_block, and ndis_open_block. It implements a hierarchical structure.
I basically talked about it. I don't know whether it is clear or not. Look forward to your feedback and guidance (tsu00@263.net ).