[Windows驅動開發](四)內存管理


一、內存管理概念
1. 物理內存概念(Physical Memory Address)
    PC上有三條總線,分別是數據總線、地址總線和控制總線。32位CPU的尋址能力為4GB(2的32次方)個字節。用戶最多可以使用4GB的真實物理內存。PC中很多設備都提供了自己的設備內存。這部分內存會映射到PC的物理內存上,也就是讀寫這段物理地址,其實讀寫的是設備內存地址,而不是物理內存地址。

2. 虛擬內存概念
    雖然可以尋址4GB的內存,但是PC中往往沒有如此多的真實物理內存。操作系統和硬件(主要是CPU中的內存管理單元MMU)為使用者提供了虛擬內存的概念。Windows的所有程序可以操作的都是虛擬內存。對虛擬內存的所有操作最終都會被轉換成對真實物理內存的操作。
    CPU中有一個重要的寄存器CR0,它是一個32位寄存器,其中的PG位負責標記是否分頁。Windows在啟動前會將它設置為1,即允許分頁。WDK中有一個宏PAGE_SIZE記錄分頁大小,一般為4KB。4GB的虛擬內存會被分割成1M個分頁單元。
    其中,有一部分單元會和物理內存對應起來,即虛擬內存中第N個分頁單元對應着物理內存的第M個分頁單元。這種對應不是一一對應,而是多對一的映射,多個虛擬內存頁可以映射同一個物理內存頁。還有一部分單元會被映射成磁盤上的一個文件,並被標記為“臟的(Dirty)”。讀取這段虛擬內存的時候,系統會發出一個異常,此時會觸發異常處理函數,異常處理函數會將這個頁的磁盤文件讀入內存,並將其標記設置為“不臟”。讓經常不讀寫的內存頁交換(Swap)成文件,並將此頁設置為“臟”。還有一部分單元什么也沒有對應,為空。
    Windows如此設計是因為以下兩種原因:
        a. 虛擬的增加了內存的大小。
        b. 使不同進程的虛擬內存互不干擾。

3. 用戶態地址和內核態地址
    虛擬地址在0~0x7fffffff范圍內的虛擬內存,即低2GB的虛擬地址,被稱為用戶態地址。而0x80000000~0xffffffff范圍內的虛擬內存,即高2GB的虛擬內存,被稱為內核態地址。Windows規定運行在用戶態(Ring3層)的程序只能訪問用戶態地址,而運行在內核態(Ring0層)的程序可以訪問整個4GB的虛擬內存。
    Windows的核心代碼和Windows的驅動程序加載的位置都是在高2GB的內核地址中。Windows操作系統在進程切換時,保持內核態地址是完全相同的,即所有進程的內核地址映射完全一致,進程切換時只改變用戶模式地址的映射。

4. Windows驅動程序和進程的關系
    驅動程序類似於一個DLL,被應用程序加載到虛擬內存中,只不過加載地址是內核地址。它能訪問的只是這個進程的虛擬內存,不能訪問其他進程的虛擬地址。Windows驅動程序里的不同例程運行在不同的進程中。DriverEntry例程和AddDevice例程是運行在系統(System)進程中的。這個進程是Windows第一個運行的進程。當需要加載的時候,這個進程中會有一個線程將驅動程序加載到內核模式地址空間內,並調用DriverEntry例程。
    其他的例程,如IRP的派遣函數會運行於應用程序的“上下文”中。“上下文”是指運行於某個進程的環境中,所能訪問的虛擬地址是這個進程的虛擬地址。
    在內核態通過調用PsGetCurrentProcess()函數得到當前IO活動的進程,它是EPROCESS的結構體,其中包含了進程的相關信息。由於微軟沒有公開EPROCESS結構體,所以不同的系統需要使用Windbg查看其具體的值。在Win XP SP2中這個結構的0x174偏移處記錄了一個字符串指針,表示的是進程的映像名稱。

5. 分頁與非分頁內存
    Windows規定有些虛擬內存頁面是可以交換到文件中的,這類內存被稱為分頁內存。而有些虛擬內存頁永遠也不會交換到文件中,這些內存被稱為非分頁內存。
    當程序的中斷請求級在DISPATCH_LEVEL之上時(包括DISPATCH_LEVEL層),程序只能使用非分頁內存,否則將導致系統藍屏死機。
    在編譯WDK提供的例程時,可以指定某個例程和某個全局變量是載入分頁內存還是非分頁內存,需要做如下定義:

  1. //  
  2.   
  3. #define PAGEDCODE code_seg("PAGE")  
  4. #define LOCKEDCODE code_seg()  
  5. #define INITCODE code_seg("INIT")  
  6.   
  7. #define PAGEDDATA code_seg("PAGE")  
  8. #define LOCKEDDATA code_seg()  
  9. #define INITDATA code_seg("INIT")  
  10.   
  11. //   
//

#define PAGEDCODE code_seg("PAGE")
#define LOCKEDCODE code_seg()
#define INITCODE code_seg("INIT")

#define PAGEDDATA code_seg("PAGE")
#define LOCKEDDATA code_seg()
#define INITDATA code_seg("INIT")

// 

    如果將某個函數載入到分頁內存中,我們需要在函數的實現中加入如下代碼:

  1. //  
  2.   
  3. #pragma PAGEDCODE  
  4. VOID SomeFunction()  
  5. {  
  6.     PAGED_CODE();  
  7.     // Do any other things ....  
  8. }  
  9.   
  10. //  
//

#pragma PAGEDCODE
VOID SomeFunction()
{
    PAGED_CODE();
    // Do any other things ....
}

//

    其中,PAGED_CODE()是WDK提供的宏,只在check版本中生效。他會檢測這個函數是否運行低於DISPATCH_LEVEL的中斷請求級,如果等於或高於這個中斷請求級,將產生一個斷言。
    如果讓函數加載到非分頁內存中,需要在函數的實現中加入如下代碼:

  1. //  
  2.   
  3. #pragma LOCKEDCODE  
  4. VOID SomeFunction()  
  5. {  
  6.     // Do any other things ....  
  7. }  
  8.   
  9. //  
//

#pragma LOCKEDCODE
VOID SomeFunction()
{
    // Do any other things ....
}

//

    還有一些特殊的情況,當某個例程在初始化的時候載入內存,然后就可以從內存中卸載掉。這種情況特指在調用DriverEntry的時候。尤其是NT式驅動,它會很長,占用很大的空間,為了節省內存,需要及時的從內存中卸載掉。代碼如下:

  1. //  
  2.   
  3. #pragma INITCODE  
  4. extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath)  
  5. {  
  6.     // Do any other things ....  
  7. }  
  8.   
  9. //  
//

#pragma INITCODE
extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath)
{
    // Do any other things ....
}

//

6. 分配內核內存
    Windows驅動程序使用的內存資源非常珍貴,分配內存時要盡量節約。和應用程序一樣,局部變量是存放在棧(Stack)空間中的。但是棧空間不會像應用程序那么大,所以驅動程序不適合遞歸調用或者局部變量是大型結構體。如果需要大型結構體,需要在堆(Heap)中申請。
    堆中申請內存的函數有以下幾個:

  1. //  
  2.   
  3. NTKERNELAPI  
  4. PVOID  
  5. ExAllocatePool(  
  6.     __drv_strictTypeMatch(__drv_typeExpr) __in POOL_TYPE PoolType,  
  7.     __in SIZE_T NumberOfBytes  
  8.     );  
  9.   
  10. NTKERNELAPI  
  11. PVOID  
  12. NTAPI  
  13. ExAllocatePoolWithTag(  
  14.     __in __drv_strictTypeMatch(__drv_typeExpr) POOL_TYPE PoolType,  
  15.     __in SIZE_T NumberOfBytes,  
  16.     __in ULONG Tag  
  17.     );  
  18.   
  19. NTKERNELAPI  
  20. PVOID  
  21. ExAllocatePoolWithQuota(  
  22.     __drv_strictTypeMatch(__drv_typeExpr) __in POOL_TYPE PoolType,  
  23.     __in SIZE_T NumberOfBytes  
  24.     );  
  25.   
  26. NTKERNELAPI  
  27. PVOID  
  28. ExAllocatePoolWithQuotaTag(  
  29.     __in __drv_strictTypeMatch(__drv_typeExpr) POOL_TYPE PoolType,  
  30.     __in SIZE_T NumberOfBytes,  
  31.     __in ULONG Tag  
  32.     );  
  33.   
  34. //  
//

NTKERNELAPI
PVOID
ExAllocatePool(
    __drv_strictTypeMatch(__drv_typeExpr) __in POOL_TYPE PoolType,
    __in SIZE_T NumberOfBytes
    );

NTKERNELAPI
PVOID
NTAPI
ExAllocatePoolWithTag(
    __in __drv_strictTypeMatch(__drv_typeExpr) POOL_TYPE PoolType,
    __in SIZE_T NumberOfBytes,
    __in ULONG Tag
    );

NTKERNELAPI
PVOID
ExAllocatePoolWithQuota(
    __drv_strictTypeMatch(__drv_typeExpr) __in POOL_TYPE PoolType,
    __in SIZE_T NumberOfBytes
    );

NTKERNELAPI
PVOID
ExAllocatePoolWithQuotaTag(
    __in __drv_strictTypeMatch(__drv_typeExpr) POOL_TYPE PoolType,
    __in SIZE_T NumberOfBytes,
    __in ULONG Tag
    );

//

    ● PoolType:枚舉變量。如果為NonPagedPool,則分配非分頁內存。如果為PagedPool,則分配分頁內存。
    ● NumberOfBytes:分配內存的大小。 注:最好是4的倍數。
    ● 返回值:分配內存的地址,一定是內核模式地址。如果返回0則代表分配失敗。

    以上四個函數功能類似。以WithQuota結尾的函數代表分配的時候按配額分配。以WithTag結尾的函數和ExAllocatePool功能類似,唯一不同的是多了一個tag參數,系統在要求的內存外額外地多分配了4字節的標簽。在調試的時候,可以找到是否有標有這個標簽的內存沒有被釋放。
    以上4個函數都需要指定PoolType,分別可以指定如下幾種:
    ● NonPagedPool:指定要求分配非分頁內存。
    ● PagedPool:指定要求分配分頁內存。
    ● NonPagedPoolMustSucceed:指定分配非分頁內存,必須成功。
    ● DontUseThisType:未指定。
    ● NonPagedPoolCacheAligned:指定要求分配非分頁內存,而且必須內存對齊。
    ● PagedPoolCacheAligned:指定分配分頁內存,而且必須內存對齊。
    ● NonPagedPoolCacheAlignedMustS:指定分配非分頁內存,而且必須對齊,且必須成功。

    將分配的內存進行回收的函數是ExFreePool和ExFreePoolWithTag,他們的原型是:

  1. //  
  2.   
  3. NTKERNELAPI  
  4. VOID  
  5. ExFreePoolWithTag(  
  6.     __in __drv_freesMem(Mem) PVOID P,   // 要釋放的地址  
  7.     __in ULONG Tag  
  8.     );  
  9.   
  10. #define ExFreePool(a) ExFreePoolWithTag(a,0)  
  11.   
  12. //  
//

NTKERNELAPI
VOID
ExFreePoolWithTag(
    __in __drv_freesMem(Mem) PVOID P,   // 要釋放的地址
    __in ULONG Tag
    );

#define ExFreePool(a) ExFreePoolWithTag(a,0)

//


二、在驅動中使用鏈表

    WDK提供了兩種鏈表:單向鏈表、雙向鏈表。
    單項鏈表每個元素有一個Next指針指向下一個元素。雙向鏈表每隔元素有兩個指::BLINK指向前一個元素,FLINK指向下一個元素。

1. 鏈表結構
    
  1. // WDK中定義的雙向鏈表數據結構  
  2.   
  3. //  
  4. //  Doubly linked list structure.  Can be used as either a list head, or  
  5. //  as link words.  
  6. //  
  7.   
  8. typedef struct _LIST_ENTRY {  
  9.    struct _LIST_ENTRY *Flink;  
  10.    struct _LIST_ENTRY *Blink;  
  11. } LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY;  
  12.   
  13. //  
  14. //  Singly linked list structure. Can be used as either a list head, or  
  15. //  as link words.  
  16. //  
  17.   
  18. typedef struct _SINGLE_LIST_ENTRY {  
  19.     struct _SINGLE_LIST_ENTRY *Next;  
  20. } SINGLE_LIST_ENTRY, *PSINGLE_LIST_ENTRY;  
  21.   
  22. //   
// WDK中定義的雙向鏈表數據結構

//
//  Doubly linked list structure.  Can be used as either a list head, or
//  as link words.
//

typedef struct _LIST_ENTRY {
   struct _LIST_ENTRY *Flink;
   struct _LIST_ENTRY *Blink;
} LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY;

//
//  Singly linked list structure. Can be used as either a list head, or
//  as link words.
//

typedef struct _SINGLE_LIST_ENTRY {
    struct _SINGLE_LIST_ENTRY *Next;
} SINGLE_LIST_ENTRY, *PSINGLE_LIST_ENTRY;

// 

2. 鏈表初始化
    初始化鏈表頭用InitializeListHead宏實現。讓雙向鏈表的兩個指針都指向自己。
    判斷鏈表是否為空,只用判斷鏈表指針是否指向自己即可。WDK提供了一個IsListEmpty。
    程序員需要自己定義鏈表每個元素的數據類型,並將LIST_ENTRY結構作為自動以結構的一個子域。LIST_ENTRY的作用是將自定義的數據結構串成一個鏈表。

  1. //  
  2.   
  3. typedef struct _MYDATASTRUCT{  
  4.     // List Entry要作為_MYDATASTRUCT結構體的一部分  
  5.     LIST_ENTRY ListEntry;  
  6.   
  7.     // 自己定義的數據  
  8.     ULONG x;  
  9.     ULONG y;  
  10. };  
  11.   
  12. //  
//

typedef struct _MYDATASTRUCT{
    // List Entry要作為_MYDATASTRUCT結構體的一部分
    LIST_ENTRY ListEntry;

    // 自己定義的數據
    ULONG x;
    ULONG y;
};

//

3. 從首部插入鏈表
    在頭部插入鏈表使用語句InsertHeadList。

  1. //  
  2.   
  3. InsertHeadList(&head, &mydata->ListEntry);  
  4.   
  5. //  
//

InsertHeadList(&head, &mydata->ListEntry);

//

    head是LIST_ENTRY結構的鏈表頭,mydata是用戶定義的數據結構,它的子域ListEntry是包含其中的LIST_ENTRY數據結構。

4. 從尾部插入鏈表
    在尾部插入鏈表使用語句InsertTailList。

  1. //  
  2.   
  3. InsertTailList(&head, &mydata->ListEntry);  
  4.   
  5. //  
//

InsertTailList(&head, &mydata->ListEntry);

//

    head是LIST_ENTRY結構的鏈表頭,mydata是用戶定義的數據結構,它的子域ListEntry是包含其中的LIST_ENTRY數據結構。

5. 從鏈表刪除
    從鏈表刪除元素也是分兩種。一種是從鏈表頭部刪除,一種是從鏈表尾部刪除。分別隊形RemoveHeadList和RemoveTailList函數。

  1. //  
  2.   
  3. PLIST_ENTRY pEntry = RemoveHeadList(&head);  
  4. PLIST_ENTRY pEntry = RemoveTailList(&tail);  
  5.   
  6. //  
//

PLIST_ENTRY pEntry = RemoveHeadList(&head);
PLIST_ENTRY pEntry = RemoveTailList(&tail);

//

    head是鏈表頭,pEntry是從鏈表刪除下來的元素中的ListEntry。
    如果用戶自定義的數據結構第一個字段是LIST_ENTRY時,返回的指針可以強制轉換為用戶的數據結構指針。
    如果第一個字段不是LIST_ENTRY時,需要減去偏移量。為了簡化操作WDK提供了宏CONTAINING_RECORD,其用法如下:

  1. //  
  2.   
  3. PLIST_ENTRY pEntry = RemoveHeadList(&head);  
  4. PIRP pIrp = CONTAINING_RECORD(pEntry, MYDATASTRUCT, ListEntry);  
  5.   
  6. //  
//

PLIST_ENTRY pEntry = RemoveHeadList(&head);
PIRP pIrp = CONTAINING_RECORD(pEntry, MYDATASTRUCT, ListEntry);

//

ListEntry為自定義的數據結構指針。


三、 Lookaside結構

    頻繁申請和回收內存,會導致在內存上產生大量內存“空洞”,導致無法申請新的內存。WDK為程序員提供了Lookaside結構來解決此問題。

1. 頻繁申請內存的弊端
    頻繁的申請與釋放內存,會導致內存產生大量碎片。即使內存中有大量的可用內存,也會導致沒有足夠的連續內存空間而導致申請內存失敗。在操作系統空閑的時候,系統會整理內存中的碎片,將碎片合並。

2. 使用Lookaside
    Lookaside對象可以理解成一個內存容器。在初始的時候,它先向Windows申請量一塊比較大的內存。以后程序員每次申請的時候就不直接向Windows申請內存了,而是直接向Lookaside對象申請呢村。Lookaside對象智能的避免產生內存碎片。
    如果Lookaside內部內存不夠用時它會向操作系統申請更多的內存。當Lookaside有大量內存未被使用時,它會讓Windows回收部分內存。使用Lookaside申請內存效率要高於直接向Windows申請內存。
    Lookaside一般在以下情況使用:
    a. 程序員每次申請固定大小的內存;
    b. 申請和回收操作非常頻繁。

    使用Lookaside對象,首先要進行初始化:

  1. // WDK提供的Lookaside初始化函數  
  2.   
  3. VOID ExInitializeNPagedLookasideList(  
  4.     IN PNPAGED_LOOKASIDE_LIST  Lookaside,  
  5.     IN PALLOCATE_FUNCTION  Allocate  OPTIONAL,  
  6.     IN PFREE_FUNCTION  Free  OPTIONAL,  
  7.     IN ULONG  Flags,  
  8.     IN SIZE_T  Size,  
  9.     IN ULONG  Tag,  
  10.     IN USHORT  Depth);  
  11.   
  12. VOID ExInitializePagedLookasideList(  
  13.     IN PPAGED_LOOKASIDE_LIST  Lookaside,  
  14.     IN PALLOCATE_FUNCTION  Allocate  OPTIONAL,  
  15.     IN PFREE_FUNCTION  Free  OPTIONAL,  
  16.     IN ULONG  Flags,  
  17.     IN SIZE_T  Size,  
  18.     IN ULONG  Tag,  
  19.     IN USHORT  Depth);  
  20.   
  21. //   
// WDK提供的Lookaside初始化函數

VOID ExInitializeNPagedLookasideList(
    IN PNPAGED_LOOKASIDE_LIST  Lookaside,
    IN PALLOCATE_FUNCTION  Allocate  OPTIONAL,
    IN PFREE_FUNCTION  Free  OPTIONAL,
    IN ULONG  Flags,
    IN SIZE_T  Size,
    IN ULONG  Tag,
    IN USHORT  Depth);

VOID ExInitializePagedLookasideList(
    IN PPAGED_LOOKASIDE_LIST  Lookaside,
    IN PALLOCATE_FUNCTION  Allocate  OPTIONAL,
    IN PFREE_FUNCTION  Free  OPTIONAL,
    IN ULONG  Flags,
    IN SIZE_T  Size,
    IN ULONG  Tag,
    IN USHORT  Depth);

// 

    這兩個函數分別是對非分頁內存和分頁內存的申請。內存回收可用以下函數

  1. //   
  2.   
  3. VOID   
  4.   ExFreeToNPagedLookasideList(  
  5.     IN PNPAGED_LOOKASIDE_LIST  Lookaside,  
  6.     IN PVOID  Entry);  
  7.   
  8. VOID   
  9.   ExFreeToPagedLookasideList(  
  10.     IN PPAGED_LOOKASIDE_LIST  Lookaside,  
  11.     IN PVOID  Entry);  
  12.   
  13. //   
// 

VOID 
  ExFreeToNPagedLookasideList(
    IN PNPAGED_LOOKASIDE_LIST  Lookaside,
    IN PVOID  Entry);

VOID 
  ExFreeToPagedLookasideList(
    IN PPAGED_LOOKASIDE_LIST  Lookaside,
    IN PVOID  Entry);

// 

    它們是用於回收非分頁內存與分頁內存。
    在使用完Lookaside對象后,需要刪除Lookaside對象,有以下兩個函數:

  1. //   
  2.   
  3. VOID ExDeleteNPagedLookasideList(IN PNPAGED_LOOKASIDE_LIST  Lookaside);  
  4.   
  5. VOID ExDeletePagedLookasideList(IN PPAGED_LOOKASIDE_LIST  Lookaside);  
  6.   
  7. //   
// 

VOID ExDeleteNPagedLookasideList(IN PNPAGED_LOOKASIDE_LIST  Lookaside);

VOID ExDeletePagedLookasideList(IN PPAGED_LOOKASIDE_LIST  Lookaside);

// 

    這兩個函數分別刪除非分頁與分頁的Lookaside對象。

 
 

Lookaside結構

頻繁的申請和回收內存,會導致在內存上產生大量的內存“空洞”,從而導致最終無法申請內存。DDK為程序員提供了Lookaside結構來解決這個問題。

我們可以將Lookaside對象看成是一個內存容器。在初始化的時候,它先向Windows申請了一塊比較大的內存。以后程序員每次申請內存的時候,不是直接向Windows申請內存,而是想Lookaside對象申請內存。Looaside會智能的避免產生內存“空洞”。如果Lookaside對象內部內存不夠用時,它會向操作系統申請更多的內存。

Lookaside一般會在以下情況下使用:

1.       程序員每次申請固定大小的內存。

2.       申請和回收的操作十分頻繁。

 

要使用Looaside對象,首先要初始化Lookaside對象,有以下兩個函數可以使用:

(1)VOID    ExInitializeNPagedLookasideList(     IN PNPAGED_LOOKASIDE_LIST  Lookaside,     IN PALLOCATE_FUNCTION  Allocate  OPTIONAL,     IN PFREE_FUNCTION  Free  OPTIONAL,     IN ULONG  Flags,     IN SIZE_T  Size,     IN ULONG  Tag,     IN USHORT  Depth     );

 

(2)VOID    ExInitializePagedLookasideList(     IN PPAGED_LOOKASIDE_LIST  Lookaside,     IN PALLOCATE_FUNCTION  Allocate  OPTIONAL,     IN PFREE_FUNCTION  Free  OPTIONAL,     IN ULONG  Flags,     IN SIZE_T  Size,     IN ULONG  Tag,     IN USHORT  Depth     );

 

初始化玩Lookaside對象后,可以進行申請內存的操作了:

(1)PVOID      ExAllocateFromNPagedLookasideList(     IN PNPAGED_LOOKASIDE_LIST  Lookaside     );

 

(2)PVOID    ExAllocateFromPagedLookasideList(     IN PPAGED_LOOKASIDE_LIST  Lookaside     );

 

對Lookaside對象回收內存:

(1)VOID    ExFreeToNPagedLookasideList(     IN PNPAGED_LOOKASIDE_LIST  Lookaside,     IN PVOID  Entry     );

 

(2)VOID    ExFreeToPagedLookasideList(     IN PPAGED_LOOKASIDE_LIST  Lookaside,     IN PVOID  Entry     );

 

在使用完Lookaside對象后,要刪除Lookaside對象:

(1)VOID    ExDeleteNPagedLookasideList(     IN PNPAGED_LOOKASIDE_LIST  Lookaside     );

 

(2) VOID    ExDeletePagedLookasideList(     IN PPAGED_LOOKASIDE_LIST  Lookaside     );

 

測試代碼:

#pragma INITCODE

VOID LookasideTets()

{

     KdPrint(("進入LookasideTest函數!\n"));

     PAGED_LOOKASIDE_LIST  Lookaside;

     ExInitializePagedLookasideList(&Lookaside,NULL, NULL, 0, sizeof(MYDATASTRUCT), 'abcd', 0);

     PMYDATASTRUCT pMyData[50];

     for (inti=0; i<50; i++)

     {

         pMyData[i] = (PMYDATASTRUCT)ExAllocateFromPagedLookasideList(&Lookaside);

         if ((i+1)%10 == 0)

         {

 

              KdPrint(("申請了 %d 個數據了!\n", ++i));

         }

     }

     for (inti=0; i<50; i++)

     {

         ExFreeToPagedLookasideList(&Lookaside,pMyData[i]);

         pMyData[i] =NULL;

         if ((i+1)%10 == 0)

         {

              KdPrint(("釋放了 %d 個數據的內存了!\n", ++i));

         }

     }

     ExDeletePagedLookasideList(&Lookaside);

}

 

 

2.運行時函數

(1)內存間復制(非重疊)

VOID    RtlCopyMemory(     IN VOID UNALIGNED  *Destination,     IN CONST VOID UNALIGNED  *Source,     IN SIZE_T  Length     );

 

(2)內存間復制(可重疊)

VOID    RtlMoveMemory(     IN VOID UNALIGNED  *Destination,     IN CONST VOID UNALIGNED  *Source,     IN SIZE_T  Length     );

 

(3)填充內存

VOID    RtlFillMemory(     IN VOID UNALIGNED  *Destination,     IN SIZE_T  Length,     IN UCHAR  Fill     );

 

VOID    RtlZeroMemory(     IN VOID UNALIGNED  *Destination,     IN SIZE_T  Length     );

 

(4)內存比較

SIZE_T    RtlCompareMemory(     IN CONST VOID  *Source1,     IN CONST VOID  *Source2,     IN SIZE_T  Length     );

 

ULONG   RtlEqualMemory(      CONST VOID  *Source1,      CONST VOID  *Source2,      SIZE_T  Length      );

 

測試代碼:

#define BUFFER_SIZE 1024

#pragma INITCODE

VOID RtlTest()

{

     KdPrint(("進入RtlTest函數!\n"));

     PUCHAR pBuffer1 = (PUCHAR)ExAllocatePool(PagedPool,BUFFER_SIZE);

     RtlZeroMemory(pBuffer1,BUFFER_SIZE);

     PUCHAR pBuffer2 = (PUCHAR)ExAllocatePool(PagedPool,BUFFER_SIZE);

     RtlFillMemory(pBuffer2,BUFFER_SIZE, 0xAA);

     RtlCopyMemory(pBuffer1,pBuffer2, BUFFER_SIZE);

    

     if (RtlEqualMemory(pBuffer1,pBuffer2, BUFFER_SIZE))

     {

         KdPrint(("兩塊內存塊數據一樣!\n"));

         for(inti=0; i<BUFFER_SIZE;i++)

         {

              KdPrint(("%02X", pBuffer1[i]));

         }

        

     }

     else

     {

         KdPrint(("兩塊內存塊數據不一樣!\n"));

     }

     KdPrint(("離開RtlTest函數!\n"));

}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM