日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

Windows 内核数据结构学习总结

發布時間:2025/4/14 windows 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Windows 内核数据结构学习总结 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

《Windows內核編程》---基本數據結構



驅動對象:
每個驅動程序都會有唯一的驅動對象與之對應,并且這個驅動對象是在驅動加載時被內核中的對象管理程序所創建的。驅動對象用


DRIVER_OBJECT數據結構表示,它作為驅動的一個實例被內核加載,并且內核對一個驅動只加載一個實例。確切地說,是由內核中的I/O


管理器負責加載的,驅動程序需要在DriverEntry中初始化。驅動對象的結構定義如下(wdm.h):
typedef struct _DRIVER_OBJECT {
? ? //結構的類型和大小
? ? CSHORT Type;
CSHORT Size;
?
//每個驅動程序會有一個或多個設備對象,其中,每個設備對象都有一個指針指向下一個驅動對象
//最后一個設備對象指向空。DeviceObject指向驅動對象的第一個設備對象。通過DeviceObject,就
//可以遍歷驅動對象中的所有設備對象了。
? ? PDEVICE_OBJECT DeviceObject;
? ? ULONG Flags;
??
? ? // The following section describes where the driver is loaded. ?The count
? ? // field is used to count the number of times the driver has had its
? ? // registered reinitialization routine invoked.
? ? PVOID DriverStart;
? ? ULONG DriverSize;
? ? PVOID DriverSection;
? ? PDRIVER_EXTENSION DriverExtension;
?
? ? // 記錄驅動設備的名字,用UNICODE字符串記錄,該字符串一般/Driver/[驅動程序名稱]
? ? UNICODE_STRING DriverName;
?
//設備的硬件數據庫鍵名,也是UNICODE字符串記錄。一般為
// ?/REGISTRY/MACHINE/HADRWARE/DESCRIPTION/SYSTEM
? ? PUNICODE_STRING HardwareDatabase;
?
? ? //文件驅動中用到的派遣函數
? ? PFAST_IO_DISPATCH FastIoDispatch;
?
? ? // The following section describes the entry points to this particular
? ? // driver. ?Note that the major function dispatch table must be the last
? ? // field in the object so that it remains extensible.
PDRIVER_INITIALIZE DriverInit;
//記錄StartIO例程的函數地址,用于串行化操作
PDRIVER_STARTIO DriverStartIo;
//指定驅動卸載時所用的回調函數地址
PDRIVER_UNLOAD DriverUnload;
//MajorFunction域記錄的是一個函數指針數組,也就是MajorFunction是一個數組,數組中的每個
//成員記錄著一個指針,每一個指針指向的是一個函數。這個函數是處理IRP的派遣函數
? ? PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];
} DRIVER_OBJECT;
typedef struct _DRIVER_OBJECT *PDRIVER_OBJECT;
實際上如果寫一個驅動程序,或者說編寫一個內核模塊,要在Windows中加載,就必須填寫上面的結構,來告訴Windows程序提供的功能


。注意內核模塊并不生成一個進程,它只是寫一組回調函數讓Windows調用,且這組回調函數必須符合Windows內核規定的格式。上面代


碼中的“快速IO分發函數”FastIoDispatch和“普通分發函數”MajorFunction就是這樣一種回調函數。這些函數用來處理發送給這個內


核模塊的請求。
Windows中很多組件都擁有自己的DRIVER_OBJECT,例如:所有的硬件驅動程序、所有的類驅動(Disk、Cdrom…)、文件系統(NTFS和


FastFat,有各自的DRIVER_OBJECT),以及許多其他的內核組件。我們可以使用一個軟件WinObj來查看所有的內核對象。
?
設備對象:
每個驅動程序會創建一個或多個設備對象,用DEVICE_OBJECT數據結構表示。每個設備對象都會有一個指針指向下一個設備對象,因此就


形成一個設備鏈。設備鏈的第一個設備是由DRIVER_OBJECT結構體中指明的。
設備對象是內核中的重要對象,其重要性不亞于Windows GUI編程中的窗口。窗口是唯一可以接收消息的對象,任何消息都是發送到一個


窗口中的;而在內核編程中,大部分“消息”是以請求IRP的方式傳遞的。而設備對象(DEVICE_OBJECT)是唯一可以接收請求的實體,


任何一個請求IRP都是發送給某個設備對象的。
設備對象的結構是DEVICE_OBJECT,常常被簡稱為DO。一個DO可以代表很多不同的東西,例如一個實際的硬盤、或實現一個類似管道的功


能等等。我們總是在內核程序中生成一個DO,而一個內核程序是用一個驅動對象表示的,因此,一個設備對象總是屬于一個驅動對象。
在WDK的wdm.h文件中DO的定義如下:
typedef struct DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT)
_DEVICE_OBJECT {
? ? //結構的類型和大小
? ? CSHORT Type;
USHORT Size;
?
//引用計數
LONG ReferenceCount;
?
//指向驅動程序中的驅動對象,同屬于一個驅動程序的驅動對象指向的是同一驅動對象
struct _DRIVER_OBJECT *DriverObject;
?
//下一個設備對象。
//這里指的下一個設備對象是同屬于一個驅動對象的設備,也就是同一個驅動程序創建的若干設備
//對象,每個設備對象根據NextDevice域形成鏈表,從而可以枚舉每個設備對象
struct _DEVICE_OBJECT *NextDevice;
//指向下一個設備對象,這里指的是,如果有更高一層的驅動附加到這個驅動的時候
//AttachedDevice指向的就是那個更高一層的驅動
struct _DEVICE_OBJECT *AttachedDevice;
//在使用StartIO例程的時候,此域指向的是當前IRP結構
? ? struct _IRP *CurrentIrp;
PIO_TIMER Timer;
?
//此域是一個32位的無符號整型,每一位有具體的含義
//DO_BUFFERED_IO---讀寫操作使用緩沖方式(系統復制緩沖區)訪問用戶模式數據
//DO_EXCLUSIVE---一次只允許一個線程打開設備句柄
//DO_DIRECT_IO---讀寫操作使用直接方式(內存描述符表)訪問用戶模式數據
//DO_DEVICE_INITIALIZING---設備對象正在初始化
//DO_POWER_PAGABLE---必須在PASSIVE_LEVEL級上處理IRP_MJ_PNP請求
//DO_POWER_INRUSH---設備上電期間需要大電流
? ? ULONG Flags;
? ? ULONG Characteristics;
__volatile PVPB Vpb;
//指向設備擴展對象,每個設備都會指定一個設備擴展對象,設備擴展對象記錄的是設備自己
//特殊定義的結構體,即程序員自己定義的結構體。另外,在驅動開發中,應該盡量避免全局變量的
//使用,因為全局變量涉及不容易同步問題。解決的方法是:將全局變量存在設備擴展中
PVOID DeviceExtension;
?
//設備類型,當制作虛擬設備時,應選擇FILE_DEVICE_UNKNOWN類型的設備
DEVICE_TYPE DeviceType;
?
//IRP棧大小。在多層驅動情況下,驅動與驅動之間會形成類似堆棧的結構,IRP會依次從
//最高層傳遞到最底層
? ? CCHAR StackSize;
? ? union {
? ? ? ? LIST_ENTRY ListEntry;
? ? ? ? WAIT_CONTEXT_BLOCK Wcb;
} Queue;
//設備在大容量傳輸時,需要內存對齊,以保證傳輸速度
? ? ULONG AlignmentRequirement;
? ? KDEVICE_QUEUE DeviceQueue;
KDPC Dpc;
?
? ? // ?The following field is for exclusive use by the filesystem to keep
? ? // ?track of the number of Fsp threads currently using the device
? ? ULONG ActiveThreadCount;
? ? PSECURITY_DESCRIPTOR SecurityDescriptor;
? ? KEVENT DeviceLock;
?
? ? USHORT SectorSize;
? ? USHORT Spare1;
?
? ? struct _DEVOBJ_EXTENSION ?*DeviceObjectExtension;
? ? PVOID ?Reserved;
} DEVICE_OBJECT;
typedef struct _DEVICE_OBJECT *PDEVICE_OBJECT;
一個驅動對象可以生成多個設備對象,而Windows向設備對象發送請求時,這些請求是被驅動對象的分發函數所捕獲的,即當Windows內


核向一個設備發送一個請求時,驅動對象的分發函數中的某一個會被調用,分發函數原型如下:
//參數device是請求的目標設備;參數irp是請求的指針
NTSTATUE ASCEDispatch(PDEVICE_OBJECT device, PIRP irp);
?
附:設備擴展:
設備對象記錄“通用”設備的信息,而另外一些“特殊”信息記錄在設備擴展里。各個設備擴展由程序員自己定義,每個設備的設備擴


展不盡相同。設備擴展是由程序員指定內容和大小,由I/O管理器創建的,并保存在非分頁內存中。
在驅動程序中,盡量避免使用全局函數,因為全局函數往往導致函數的不可重入性。重入性指的是在多線程程序中,多個函數并行運行


,函數的運行結果不會根據函數的調用先后順序而導致不同。解決的辦法是,將全局變量以設備擴展的形式存儲,并加以適當的同步保


護措施。除此之外,在設備擴展中還會記錄下列一些內容:
設備對象的反向指針;
設備狀態或驅動環境變量;
中斷對象指針;
控制器對象指針。
由于設備擴展是驅動程序專用的,它的結構必須在驅動程序的頭文件中定義。
?
請求IRP:
內核中大部分請求以IRP的形式發送。IRP是一個內核數據結構,比較復雜,因為它要表示無數種實際請求。在WDK的wdm.h中可看到IRP的


結構:
// I/O Request Packet (IRP) definition
typedef struct DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT) _IRP {
? ? //結構的類型和大小
? ? CSHORT Type;
? ? USHORT Size;
?
// Define the common fields used to control the IRP.
?
? ? // Define a pointer to the Memory Descriptor List (MDL) for this I/O
? ? // request. ?This field is only used if the I/O is "direct I/O".
? ? PMDL MdlAddress;
?
? ? // Flags word - used to remember various flags.
? ? ULONG Flags;
?
? ? // The following union is used for one of three purposes:
? ? //
? ? // ? ?1. This IRP is an associated IRP. ?The field is a pointer to a master
? ? // ? ? ? IRP.
? ? //
? ? // ? ?2. This is the master IRP. ?The field is the count of the number of
? ? // ? ? ? IRPs which must complete (associated IRPs) before the master can
? ? // ? ? ? complete.
? ? //
? ? // ? ?3. This operation is being buffered and the field is the address of
? ? // ? ? ? the system space buffer.
? ? union {
? ? ? ? struct _IRP *MasterIrp;
? ? ? ? __volatile LONG IrpCount;
? ? ? ? PVOID SystemBuffer;
? ? } AssociatedIrp;
?
? ? // Thread list entry - allows queueing the IRP to the thread pending I/O
? ? // request packet list.
? ? LIST_ENTRY ThreadListEntry;
?
? ? // I/O status - final status of operation.
? ? IO_STATUS_BLOCK IoStatus;
?
? ? // Requestor mode - mode of the original requestor of this operation.
? ? KPROCESSOR_MODE RequestorMode;
?
? ? // Pending returned - TRUE if pending was initially returned as the
? ? // status for this packet.
? ? BOOLEAN PendingReturned;
?
? ? // Stack state information.
? ? CHAR StackCount; ? ? ? ? //IPR棧空間大小
? ? CHAR CurrentLocation; ? ? ? ?//IRP當前棧空間
?
? ? // Cancel - packet has been canceled.
? ? BOOLEAN Cancel;
?
? ? // Cancel Irql - Irql at which the cancel spinlock was acquired.
? ? KIRQL CancelIrql;
?
? ? // ApcEnvironment - Used to save the APC environment at the time that the
? ? // packet was initialized.
? ? CCHAR ApcEnvironment;
?
? ? // Allocation control flags.
? ? UCHAR AllocationFlags;
?
? ? // User parameters.
? ? PIO_STATUS_BLOCK UserIosb;
? ? PKEVENT UserEvent;
? ? union {
? ? ? ? struct {
? ? ? ? ? ? union {
? ? ? ? ? ? ? ? PIO_APC_ROUTINE UserApcRoutine;
? ? ? ? ? ? ? ? PVOID IssuingProcess;
? ? ? ? ? ? };
? ? ? ? ? ? PVOID UserApcContext;
? ? ? ? } AsynchronousParameters;
? ? ? ? LARGE_INTEGER AllocationSize;
? ? } Overlay;
?
? ? // CancelRoutine - Used to contain the address of a cancel routine supplied
? ? // by a device driver when the IRP is in a cancelable state.
? ? __volatile PDRIVER_CANCEL CancelRoutine;
?
? ? // Note that the UserBuffer parameter is outside of the stack so that I/O
? ? // completion can copy data back into the user's address space without
? ? // having to know exactly which service was being invoked. ?The length
? ? // of the copy is stored in the second half of the I/O status block. If
? ? // the UserBuffer field is NULL, then no copy is performed.
? ? PVOID UserBuffer;
?
// Kernel structures
?
? ? // The following section contains kernel structures which the IRP needs
? ? // in order to place various work information in kernel controller system
? ? // queues. ?Because the size and alignment cannot be controlled, they are
? ? // placed here at the end so they just hang off and do not affect the
? ? // alignment of other fields in the IRP.
? ? union {
? ? ? ? struct {
? ? ? ? ? ? union {
? ? ? ? ? ? ? ? // DeviceQueueEntry - The device queue entry field is used to
? ? ? ? ? ? ? ? // queue the IRP to the device driver device queue.
? ? ? ? ? ? ? ? KDEVICE_QUEUE_ENTRY DeviceQueueEntry;
?
? ? ? ? ? ? ? ? struct {
? ? ? ? ? ? ? ? ? ? // The following are available to the driver to use in
? ? ? ? ? ? ? ? ? ? // whatever manner is desired, while the driver owns the
? ? ? ? ? ? ? ? ? ? // packet.
? ? ? ? ? ? ? ? ? ? PVOID DriverContext[4];
? ? ? ? ? ? ? ? } ;
? ? ? ? ? ? } ;
? ? ? ? ? ? // Thread - pointer to caller's Thread Control Block.
? ? ? ? ? ? PETHREAD Thread;
?
? ? ? ? ? ? // Auxiliary buffer - pointer to any auxiliary buffer that is
? ? ? ? ? ? // required to pass information to a driver that is not contained
? ? ? ? ? ? // in a normal buffer.
? ? ? ? ? ? PCHAR AuxiliaryBuffer;
?
? ? ? ? ? ? // The following unnamed structure must be exactly identical
? ? ? ? ? ? // to the unnamed structure used in the minipacket header used
? ? ? ? ? ? // for completion queue entries.
? ? ? ? ? ? struct {
? ? ? ? ? ? ? ? // List entry - used to queue the packet to completion queue, among
? ? ? ? ? ? ? ? // others.
? ? ? ? ? ? ? ? LIST_ENTRY ListEntry;
?
? ? ? ? ? ? ? ? union {
? ? ? ? ? ? ? ? ? ? // Current stack location - contains a pointer to the current
? ? ? ? ? ? ? ? ? ? // IO_STACK_LOCATION structure in the IRP stack. ?This field
? ? ? ? ? ? ? ? ? ? // should never be directly accessed by drivers. ?They should
? ? ? ? ? ? ? ? ? ? // use the standard functions.
? ? ? ? ? ? ? ? ? ? struct _IO_STACK_LOCATION *CurrentStackLocation;
?
? ? ? ? ? ? ? ? ? ? // Minipacket type.
? ? ? ? ? ? ? ? ? ? ULONG PacketType;
? ? ? ? ? ? ? ? };
? ? ? ? ? ? };
?
? ? ? ? ? ? // Original file object - pointer to the original file object
? ? ? ? ? ? // that was used to open the file. ?This field is owned by the
? ? ? ? ? ? // I/O system and should not be used by any other drivers.
? ? ? ? ? ? PFILE_OBJECT OriginalFileObject;
? ? ? ? } Overlay;
?
? ? ? ? // APC - This APC control block is used for the special kernel APC as
? ? ? ? // well as for the caller's APC, if one was specified in the original
? ? ? ? // argument list. ?If so, then the APC is reused for the normal APC for
? ? ? ? // whatever mode the caller was in and the "special" routine that is
? ? ? ? // invoked before the APC gets control simply deallocates the IRP.
? ? ? ? KAPC Apc;
?
? ? ? ? // CompletionKey - This is the key that is used to distinguish
? ? ? ? // individual I/O operations initiated on a single file handle.
? ? ? ? PVOID CompletionKey;
? ? } Tail;
} IRP;
typedef IRP *PIRP;
?
上面出現IRP棧空間,是因為一個IRP往往要傳遞n個設備才能得以完成,而在傳遞過程中會有一些“中間轉換”,導致請求的參數變化。


為了保存這些變換,我們給每次中轉都留一個“棧空間”,用于保存中間參數。
常見的請求:
生成請求:主功能號為IRP_MJ_CREATE
查詢請求:主功能號為IRP_MJ_QUERY_INFORMATION
設置請求:主功能號為IRP_MJ_SET_INFORMATION
控制請求:主功能號為IRP_MJ_DEVICE_CONTROL或IRP_MJ_INTERNAL_DEVICE_CONTROL
關閉請求:主功能號為IRP_MJ_CLOSE
?
請求指針:IRP的指針,即PIRP或IRP*
========

Windows內核常見數據結構(基本類型)

http://laokaddk.blog.51cto.com/368606/338722/
?
學內核從基本數據結構開始吧,就像學C語言時從學習int,char開始一樣.
只列出目前見到和用到的,其它后面再補充~


常用數據結構:
數字:
lkd> dt _ULARGE_INTEGER
ntdll!_ULARGE_INTEGER
? ?+0x000 LowPart ? ? ? ? ?: Uint4B
? ?+0x004 HighPart ? ? ? ? : Uint4B
? ?+0x000 u ? ? ? ? ? ? ? ?: __unnamed
? ?+0x000 QuadPart ? ? ? ? : Uint8B
lkd> dt _LARGE_INTEGER
ntdll!_LARGE_INTEGER
? ?+0x000 LowPart ? ? ? ? ?: Uint4B
? ?+0x004 HighPart ? ? ? ? : Int4B
? ?+0x000 u ? ? ? ? ? ? ? ?: __unnamed
? ?+0x000 QuadPart ? ? ? ? : Int8B
字符串:
lkd> dt _STRING
nt!_STRING
? ?+0x000 Length ? ? ? ? ? : Uint2B
? ?+0x002 MaximumLength ? ?: Uint2B
? ?+0x004 Buffer ? ? ? ? ? : Ptr32 Char


lkd> dt _UNICODE_STRING
ntdll!_UNICODE_STRING
? ?+0x000 Length ? ? ? ? ? : Uint2B
? ?+0x002 MaximumLength ? ?: Uint2B
? ?+0x004 Buffer ? ? ? ? ? : Ptr32 Uint2B
單鏈表頭(看來數據結構要學好啊)::
lkd> dt _SLIST_HEADER
ntdll!_SLIST_HEADER
? ?+0x000 Alignment ? ? ? ?: Uint8B
? ?+0x000 Next ? ? ? ? ? ? : _SINGLE_LIST_ENTRY
? ?+0x004 Depth ? ? ? ? ? ?: Uint2B
? ?+0x006 Sequence ? ? ? ? : Uint2B
鏈表結點:
lkd> dt _KNODE
ntdll!_KNODE
? ?+0x000 ProcessorMask ? ?: Uint4B
? ?+0x004 Color ? ? ? ? ? ?: Uint4B
? ?+0x008 MmShiftedColor ? : Uint4B
? ?+0x00c FreeCount ? ? ? ?: [2] Uint4B
? ?+0x018 DeadStackList ? ?: _SLIST_HEADER          //鏈表頭
? ?+0x020 PfnDereferenceSListHead : _SLIST_HEADER
? ?+0x028 PfnDeferredList : Ptr32 _SINGLE_LIST_ENTRY
? ?+0x02c Seed ? ? ? ? ? ? : UChar
? ?+0x02d Flags ? ? ? ? ? ?: _flags
單鏈表的指針:
lkd> dt _SINGLE_LIST_ENTRY
ntdll!_SINGLE_LIST_ENTRY
? ?+0x000 Next ? ? ? ? ? ? : Ptr32 _SINGLE_LIST_ENTRY
雙向鏈表指針:
lkd> dt _LIST_ENTRY
ntdll!_LIST_ENTRY
? ?+0x000 Flink ? ? ? ? ? ?: Ptr32 _LIST_ENTRY
? ?+0x004 Blink ? ? ? ? ? ?: Ptr32 _LIST_ENTRY
內核隊列:
lkd> dt _KQUEUE
ntdll!_KQUEUE
? ?+0x000 Header ? ? ? ? ? : _DISPATCHER_HEADER
? ?+0x010 EntryListHead ? ?: _LIST_ENTRY
? ?+0x018 CurrentCount ? ? : Uint4B
? ?+0x01c MaximumCount ? ? : Uint4B
? ?+0x020 ThreadListHead ? : _LIST_ENTRY
?
一個很多地方用到的頭部結構:
lkd> dt _DISPATCHER_HEADER
ntdll!_DISPATCHER_HEADER
? ?+0x000 Type ? ? ? ? ? ? : UChar
? ?+0x001 Absolute ? ? ? ? : UChar
? ?+0x002 Size ? ? ? ? ? ? : UChar
? ?+0x003 Inserted ? ? ? ? : UChar
? ?+0x004 SignalState ? ? ?: Int4B
? ?+0x008 WaitListHead ? ? : _LIST_ENTRY


========

IRP結構



?一、IRP 簡介
? ? ? ? IRP是I/O Request Pcaket 的縮寫,即I/O請求包。驅動與驅動之間通過 IRP 進行通信。而使用驅動的應用層調用的?


CreatFile,ReadFile,WriteFile,DeviceIoControl 等函數,說到底也是使用 IRP 和驅動進行通信。IRP由I/O管理器根據用戶態程序提


出的請求創建并傳給相應的驅動程序。在分層的驅動程序中,這個過程很復雜,一個IRP常常要穿越幾層驅動程序。
二、IRP結構
? ? ? ? IRP功能的復雜性也決定了IRP結構的復雜性。正確理解IRP的結構是理解驅動程序開發的基礎。另外,IRP的創建是由I/O管理器


在非分頁內存進行的。
? ? ? ? 一個IRP有兩部分組成:頭部區域和I/O堆棧位置。
? ? ? ? 1)IRP的頭部區域是一個固定的部分,起始就是一個IRP結構。
? ? ? ? 2)在這個頭部的后面是一個I/O stack locations,這是一個IO_STACK_LOCATIONS的結構體數 組,這個數組中元素的個數視具


體情況而定。由 IoAllocateIrp( IN CCHAR StackSize , IN BOOLEAN ChargeQuota ) 時的參數 StackSize 決定。而 StackSize 通常


由 IRP 發往的目標 DEVICE_OBJECT 的 +30 char StackSize 決定。而這個 StackSize 是由設備對象連入所在的設備棧時,根據在設備


棧中位置決定的。
? ? ? ?
? ? ? ?下面看看IRP結構(頭部區域)和IO_STACK_LOCATIONS(I/O堆棧)的結構的定義
? ? ? ?1. IRP結構介紹,結構圖如下,其中灰色部分為不可見區域,這里主要講解一下可見區域。
? ? ? ?
? ? ? ?1.1 PMDL ?MdlAddress : 設備執行直接I/O時,指向用戶空間的內存描述表
? ? ? ?1.2 ULONG Flags: 包含一些對驅動程序只讀的標志。但這些標志與WDM驅動程序無關
? ? ? ?1.3 AssociatedIrp.SystemBuffer : SystemBuffer指針指向一個數據緩沖區,該緩沖區位于內核模式的非分頁內存中I/O管理器


把用戶模式程序發送給驅動程序的數據復制到這個緩沖區,這也是創建IRP過程的一部分。對于讀請求,設備驅動程序把讀出的數據填到


這個緩沖區,然后I/O管理器再把緩沖區的內容復制到用戶模式緩沖區。
? ? ? ?1.4 IoStatus : 是一個結構體IO_STATUS_BLOCK, 這個結構體僅包含兩個域,驅動程序在最終完成請求時設置這個結構。
IoStatus.Status : 將收到一個NTSTATUS代碼。
IoStatus.Information 的類型為ULONG_PTR,它將收到一個信息值,該信息值的確切含義要取決于具體的IRP類型和請求完成的狀態。


Information域的一個公認用法是用于保存數據傳輸操作。某些PnP請求把這個域作為指向另外一個結構的指針,這個結構通常包含查詢


請求的結果。
? ? ? ? 1.5 RequestorMode將等于一個枚舉常量UserMode或KernelMode,指定原始I/O請求的來源。驅動程序有時需要查看這個值來決


定是否要信任某些參數。
? ? ? ? 1.6 PendingReturned(BOOLEAN)如果為TRUE,則表明處理該IRP的最低級派遣例程返回了STATUS_PENDING。完成例程通過參考該


域來避免自己與派遣例程間的潛在競爭。
? ? ? ? 1.7 Cancel(BOOLEAN)如果為TRUE,則表明IoCancelIrp已被調用,該函數用于取消這個請求。如果為FALSE,則表明沒有調用


IoCancelIrp函數。取消IRP是一個相對復雜的主題,我將在本章的最后詳細描述它。
? ? ? ? 1.8 CancelIrql(KIRQL)是一個IRQL值,表明那個專用的取消自旋鎖是在這個IRQL上獲取的。當你在取消例程中釋放自旋鎖時應


參考這個域。
? ? ? ? 1.9 CancelRoutine(PDRIVER_CANCEL)是驅動程序取消例程的地址。你應該使用IoSetCancelRoutine函數設置這個域而不是直接


修改該域。
? ? ? ? 2.0 UserBuffer(PVOID) 對于METHOD_NEITHER方式的IRP_MJ_DEVICE_CONTROL請求,該域包含輸出緩沖區的用戶模式虛擬地址。


該域還用于保存讀寫請求緩沖區的用戶模式虛擬地址,但指定了DO_BUFFERED_IO或DO_DIRECT_IO標志的驅動程序,其讀寫例程通常不需


要訪問這個域。當處理一個METHOD_NEITHER控制操作時,驅動程序能用這個地址創建自己的MDL。
? ? ? ?2. IO堆棧
? ? ??
? ? ? MajorFunction(UCHAR)是該IRP的主功能碼
? ? ? MinorFunction(UCHAR)是該IRP的副功能碼
? ? ? Parameters(union)是幾個子結構的聯合,每個請求類型都有自己專用的參數,而每個子結構就是一種參數。這些子結構包


括Create(IRP_MJ_CREATE請求)、Read(IRP_MJ_READ請求)、StartDevice(IRP_MJ_PNP的IRP_MN_START_DEVICE子類型),等等。
? ? ? DeviceObject(PDEVICE_OBJECT)是與該堆棧單元對應的設備對象的地址。該域由IoCallDriver函數負責填寫。
? ? ? FileObject(PFILE_OBJECT)是內核文件對象的地址,IRP的目標就是這個文件對象。驅動程序通常在處理清除請求


(IRP_MJ_CLEANUP)時使用FileObject指針,以區分隊列中與該文件對象無關的IRP。
? ? ? CompletionRoutine(PIO_COMPLETION_ROUTINE)是一個I/O完成例程的地址,該地址是由與這個堆棧單元對應的驅動程序的更上一


層驅動程序設置的。你絕對不要直接設置這個域,應該調用IoSetCompletionRoutine函數,該函數知道如何參考下一層驅動程序的堆棧


單元。設備堆棧的最低一級驅動程序并不需要完成例程,因為它們必須直接完成請求。然而,請求的發起者有時確實需要一個完成例程


,但通常沒有自己的堆棧單元。這就是為什么每一級驅動程序都使用下一級驅動程序的堆棧單元保存自己完成例程指針的原因。
? ? ? IO堆棧總結:
1)I/O堆棧位置的主要目的是,保存一個I/O請求的函數代碼和參數。
2)I/O堆棧數量實際上就是參與I/O請求的I/O層的數量。
3)在一個IRP中,上層驅動負責為下層驅動設置堆棧位置指針。
i)驅動程序可以為每個IRP調用IoGetCurrentStackLocation來獲得指向其自身堆棧位置的指針,
ii)上層驅動程序必須調用IoGetNextIrpStackLocation來獲得指向下層驅動程序堆棧位置的指針。
因此,上層驅動可以在傳送IRP給下層驅動之前設置堆棧位置的內容。
4)上層驅動調用IoCallDriver,將DeviceObject成員設置成下層驅動目標設備對象。當上層驅動完成IRP時,IoCompletion 函數被調用


,I/O管理器傳送給IoCompletion函數一個指向上層驅動的設備對象的指針。
?
=========================================================================================
假設某過濾驅動的分層結構如下:
1 FIDO ?<-- 此時你在這里調用IoAllocateIrp()創建一個先的IRP往下層驅動FDO傳送
2 FDO
3 PDO


假設IO堆棧單元有3個,第3個IO堆棧單元表示new_IRP當前IO堆棧單元 第2個表示FDO的IO堆棧單元 第1個表示PDO的堆棧單元那么如果要


發送此IRP到下層驅動FDO,則預先初始化new_IRP的FDO的IO堆棧單元,也就是第二個IO堆棧單元。此時new_IRP 的 StackCount = 2,?


CurrentLocation = 3 這里的3表示new_IRP的當前IO堆棧單元 如果你需要把此IRP往FDO驅動傳遞,那么不用初始化這個IO堆棧單元,為


什么呢?
因為IoCallDriver() 內部會調用 類似 IoSetNextIrpStackLocation() 的操作來調整 CurrentLocation的索引。也就是 索引-1 ;所以


要調用IoCallDriver() 把new_IRP傳遞到FDO這個下層驅動,需要先調用IoGetNextIrpStackLocation() 獲取FDO對應的IO堆棧單元,并


進行初始化。初始化完成后才能調用IoCallDriver()此時new_IRP的 CurrentLocation = 2 這個索引才是指向FDO的IO堆棧單元
下面是來自WMD一書的例子:我上面的話就是為了理解下面的代碼片斷
發往派遣例程
創建完IRP后,你可以調用IoGetNextIrpStackLocation函數獲得該IRP第一個堆棧單元的指針。然后初始化這個堆棧單元。在初始化過程


的最后,你需要填充MajorFunction代碼。堆棧單元初始化完成后,就可以調用IoCallDriver函數把IRP發送到設備驅動程序:
PDEVICE_OBJECT DeviceObject; ? ? //something gives you this
PIO_STACK_LOCATION stack = IoGetNextIrpStackLocation(Irp);
stack->MajorFunction = IRP_MJ_Xxx;


NTSTATUS status = IoCallDriver(DeviceObject, Irp);
?
IRP中的第一個堆棧單元指針被初始化成指向該堆棧單元之前的堆棧單元,因為I/O堆棧實際上是IO_STACK_LOCATION結構數組,你可以認


為這個指針被初始化為指向一個不存在的“-1”元素,因此當我們要初始化第一個堆棧單元時我們實際需要的是“下一個”堆棧單元。


IoCallDriver將沿著這個堆棧指針找到第0個表項,并提取我們放在那里的主功能代碼,在上例中為IRP_MJ_Xxx。然后IoCallDriver函數


將利用DriverObject指針找到設備對象中的MajorFunction表。IoCallDriver將使用主功能代碼索引這個表,最后調用找到的地址(派遣


函數)。
你可以把IoCallDriver函數想象為下面代碼:
NTSTATUS IoCallDriver(PDEVICE_OBJECT device, PIRP Irp)
{
? IoSetNextIrpStackLocation(Irp);
? PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
? stack->DeviceObject = device;
? ULONG fcn = stack->MajorFunction;
? PDRIVER_OBJECT driver = device->DriverObject;
? return (*driver->MajorFunction[fcn])(device, Irp);
}
========

總結

以上是生活随笔為你收集整理的Windows 内核数据结构学习总结的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。