首页 > 代码库 > [转载]设备栈

[转载]设备栈

1. 栈结构

设备栈(Device Stack)结构与内存中的栈类似,但是 device stack 中的 entry 由 device object 中的 AttachedDevice 值的连接。

如下图所示:

技术分享

并且由每个 device 的 DeviceExtension.AttachedTo 值指向下一层的 device。从而形成双向的链结构。

2. 栈顶

由 IoGetAttachedDevice() 函数来得到当前栈顶 device:

PDEVICE_OBJECT
IoGetAttachedDevice(
IN PDEVICE_OBJECT DeviceObject
)
{
//
// 直到 Top 元素的 AttachedDevice == NULL 
//
while (DeviceObject->AttachedDevice)
DeviceObject = DeviceObject->AttachedDevice;
 
return DeviceObject;

当传递给 IoGetAttachedDevice() 的 DeviceObject 已经是 Top 元素则返回 DeviceObject 值。

3. 栈底

使用 IoGetDeviceAttachmentBaseRef() 来得到栈底的 device object,IoGetDeviceAttachmentBaseRef() 将调用 IopGetDeviceAttachmentBase() 来遍历查找:

PDEVICE_OBJECT
IoGetDeviceAttachmentBaseRef(
IN PDEVICE_OBJECT  DeviceObject
)
{
KIRQL OldIrql = KeRaiseIrqlToDpcLevel();

PDEVICE_OBJECT Bottom = IopGetDeviceAttachmentBase(DeviceObject);
ObfReferenceObject(Bottom);
 
KfLowerIrql(OldIrql);

return Bottom;
}
 
 
 
PDEVICE_OBJECT
IopGetDeviceAttachmentBase(
IN PDEVICE_OBJECT  DeviceObject
)
{
//
// 根据 DeviceObjectExtension->AttachedTo 向下查找栈底
// 
while (DeviceObject->DeviceObjectExtension->AttachedTo)
{
DeviceObject = DeviceObject->DeviceObjectExtension->AttachedTo;
}

return DeviceObject;

IoGetDeviceAttachmentBaseRef() 函数根据传递的 DeviceObject 值,找到在 Bottom 元素。如果 DeviceObject 之下没有 device object 则返回参数值。

在微软的 WDK 文档里描述:当 DeviceObject 之下没有 device object 时返回 NULL,而实际的代码是返回 DeviceObject 本身(windows 2003)。

4. 挂接 device

使用 IoAttachDeviceToDeviceStack() 函数来挂接 device,它实际上调用另一个函数来完成工作:

如下代码所示:

PDEVICE_OBJECT IoAttachDeviceToDeviceStack(
        IN PDEVICE_OBJECT SourceDevice,
        IN PDEVICE_OBJECT TargetDevice
        )
{
        return IopAttachDeviceToDeviceStackSafe(
SourceDevice, 
TargetDevice, 
NULL);                // AttachedToDeviceObject 参数为 NULL
}

它简单地调用 IopAttachDeviceToDeviceStackSafe() 函数,提供第 3 个参数为 NULL 值。

下面我将它逆为 C 代码,看起来像下面这样:

PDEVICE_OBJECT IopAttachDeviceToDeviceStackSafe(
IN PDEVICE_OBJECT SourceDevice,
IN PDEVICE_OBJECT TargetDevice
OUT PDEVICE_OBJECT *AttachedToDeviceObject OPTIONAL
)
{
PDEVICE_OBJECT devObj = NULL;// 返回被 attach 的 object
PDEVICE_EXTENSION SourceDeviceExtension = SourceDevice->DeviceObjectExtension;
 
KIRQL OldIrql = KeRaiseIrqToDpcLevel();// 提升到 DISPATCH_LEVEL
 
if (IopVerifierOn == TRUE )
{
IovAttachDeviceToDeviceStack(SourceDevice, TargetDevice);
}
 
//
// 得到 stack top 的 device
//
devObj = IoGetAttachedDevice(TargetDevice);
 
PDEVICE_EXTENSION DeviceExtension = devObj->DeviceObjectExtension;
 
//
// 下面进行 attach 操作
//
if ((devObj->Flags & DO_DEVICE_INITIALIZING == 0) &&
(DeviceExtension->ExtensionFlags & (DOE_UNLOAD_PENDING | DOE_DELETE_PENDING |
DOE_REMOVE_PENDING | DOE_REMOVE_PROCESSED) == 0))
{

devObj->Spare1++;
devObj->AttachedDevice = SourceDevice;
SourceDevice->StackSize = devObj->StackSize + 1;
SourceDevice->AlignmentRequirement = devObj->AlignmentRequirement;
SourceDevice->SectorSize = devObj->SectorSize;

if (DeviceExtension->ExtensionFlags & DOE_START_PENDING)
{
SourceDevice->DeviceObjectExtension->ExtensionFlags |= DOE_START_PENDING;
}

SourceDeviceExtension->AttachedTo = devObj;
}
else 
{
devObj = NULL;
}
 
 
if (AttachedToDeviceObject != NULL)
{
*AttachedToDeviceObject = devObj;// 返回被 attach 的 device object
}
 
 
KfLowerIrql(OldIrql);         // 恢复原 IRQL 
return devObj;

IoAttachDeviceToDeviceStack() 函数的目的是将 SourceDevice 挂接在 TargetDevice 之上。那么:SourceDevice 将变为栈 Top 元素。

技术分享

成功挂接后,函数返回原 Top 元素,如图中的 device B object。

5. 移除 device

使用 IoDetachDevice() 函数来从设备栈中的元素:

VOID 
IoDetachDevice(
IN OUT PDEVICE_OBJECT  TargetDevice
)
{
KIRQL OldIrql = KeRaiseIrqlToDpcLevel();
 
if (IopVerifierOn == TRUE)
{
IovDetachDevice(TargerDevice);
}
 
//
// 删除挂接
//
TargetDevice->AttachedDevice->DeviceObjectExtension->AttachedTo = NULL; 
TargerDevice->AttachedDevice = NULL;
 
//
// 检测是否需要 Unload 或 Delete 操作
//
if ((TargetDevice->DeviceObjectExtension->ExtensionFlags & (DOE_UNLOAD_PENDING | DOE_DELETE_PENDING 
| DOE_REMOVE_PENDING)) && (TargerDevice->ReferenceCount == 0))
{
IopCompleteUnloadOrDelete(TargetDevice, FALSE, OldIrql);
}
else
{
KfLowerIrql(OldIrql);
}
}

IoDetachDevice() 函数接受的参数是由 IoAttachDeviceToDeviceStack() 挂接成功后返回的值。

技术分享

如上图所示,将移除 device B 元素。最后调用IopCompleteUnloadOrDelete() 函数做 Unload 或 Delete 操作。

[转载]设备栈