驅動雜記1:對驅動物件,裝置物件,裝置棧的理解
Windows核心採用的是面向物件的程式設計方式,但使用的確是C語言。Windows核心認為許多東西都是“物件”,
比如一個驅動一個檔案一個裝置,“物件”相當於一個基類。
一個驅動物件代表了一個驅動程式,或者說一個核心模組。驅動物件結構如下:
typedef struct _DRIVER_OBJECT{
//結構的型別和大小
CSHORT Type;
CSHORT Size;
......
//裝置物件,該驅動建立的裝置物件連結串列的開始
PDEVICE_OBJECT DeviceObject;
......
//該核心模組在核心空間中的開始地址和大小
PVOID DriverStart;
ULONG DriverSize;
......
//驅動的名字
UNICODE_STRING DriverName;
......
//FastIO例程
PFAST_IO_DISPATCH FastIoDispath;
......
//StartIO例程
PDRIVER_STARTIO DriverStartIo;
......
//解除安裝例程
PDRIVER_UNLOAD DriverUnload;
......
//普通分發函式,派遣例程
PDRIVER_DISPATH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION+1];
}DRIVER_OBJECT;
在驅動物件中我們可以看到其中有一個裝置物件成員
PDEVICE_OBJECT DeviceObject 它指向一個裝置物件連結串列的第一個物件,因為每個驅動物件可以建立若干個裝置物件,
這些裝置物件正是用連結串列形式連結起來(最後一個裝置物件指向NULL)。裝置物件即DEVICE_OBJECT ,簡稱DO。
在核心中,資訊主要以IRP(I/O請求包)方式傳遞,而裝置物件是唯一可以接受請求的實體。
那麼說到這裡我們再來看看裝置物件這個資料結構。
typedef struct _DEVICE_OBJECT{
......
//該裝置物件所屬的驅動物件
struct _DRIVER_OBJECT *DriverObject;
//水平層次上的下一個裝置物件,即和該裝置物件同屬一個驅動物件
struct _DEVICE_OBJECT *NextDevice;
//垂直層次的上一個裝置物件,和該裝置物件不屬於同一個驅動物件
struct _DEVICE_OBJECT *AttachedDevice;
//使用StartIO例程的時時候,此域指向的是當前IRP結構
struct _IRP *CurrentIrp;
ULONG Flags;
//裝置擴充套件物件,又程式設計師自己定義的結構體記錄一些裝置資訊,
//為避免使用全域性變數,可以將全域性變數存在裝置擴充套件裡面
struct _DEVOBJ_EXTENSION *DeviceObjectExtension;
}DEVICE_OBJECT;
從驅動物件和裝置物件的結構定義來看,裝置物件通過struct _DEVICE_OBJECT *NextDevice;
這個域將該驅動中的裝置物件連結起來。
驅動物件可以找到它建立的裝置連結串列頭,而裝置物件可以找到它所屬的驅動物件。
我們看到裝置物件中有一個struct _DEVICE_OBJECT *AttachedDevice成員,它是跟裝置棧相關聯的。
裝置的建立順序是,先建立底層PDO(物理裝置物件),再建立FDO(功能裝置物件),PDO和FDO之間可能還有一些過濾驅動
(稱為FiDO Filter Device Object)。
每一層裝置物件由不同的驅動物件建立,這些驅動物件共同完成一個物理裝置的驅動任務。
這由下而上生長的裝置便形成了裝置棧。*AttachedDevice就指向上一層的裝置物件,如果為NULL表示已經是最頂層。
從底層向高層可以通過*AttachedDevice來尋找,從高層到底層,裝置物件沒有提供相關子域。這裡就可以通過裝置擴充套件
這個成員來自定義,在_DEVOBJ_EXTENSION 結構中定義一個指向低一層裝置的域。