1. 程式人生 > >windows 核心程式設計之在應用程式中使用虛擬記憶體

windows 核心程式設計之在應用程式中使用虛擬記憶體

Microsoft Windows 提供了以下三種機制來對記憶體進行操控:

  • 虛擬記憶體 最適合用來管理大型物件陣列或大型結構陣列
  • 記憶體對映檔案 最適合用來管理大型資料流(通常是檔案),以及在同一臺機器上執行多個程序之間共享資料
  • 最適合管理大量小型物件

本篇只討論第一種方式 虛擬記憶體。

15.1 預訂地址空間區域

可以使用VirtualAlloc函式來預訂程序中的地址空間區域
LPVOID WINAPI VirtualAlloc(
  __in_opt  LPVOID lpAddress,
  __in      SIZE_T dwSize,
  __in      DWORD flAllocationType,
  __in      DWORD flProtect
);
lpAddress :想要預訂地址空間的哪一塊,如果要在程序地址空間第50MB的地方分配區域。我們要傳52 428 800(50*1024*1024)給pvAddress引數。系統始終按分配粒度的整數倍來分配的因此如果我們想要在19668992(300*65536+8192)的地方預訂區域,那麼系統會向下取整到64整數倍,也就是19660800(300*65536)然後取整後的地址預訂區域。如果VirtualAlloc能滿足我們的要求,那麼它會預訂一塊區域並返回該區域的基地址。如果我們還給pvAddesss指定了引數,那麼,它的返回值就等於我們傳給pvASddress引數值向下取整到64KB的整數倍
dwSize: 指定我們想要分析區域的大小,以位元組為單位。由於系統始終都根據cpu頁面大小整數倍來預訂區域,因此如果我們的頁面大小為4KB,8KB或16KB的機器上預訂62 KB的區域的話,那麼最終得到的區域大小為64KB 
flAllocationType:是預訂區域還是調撥物理儲存器,如果是預訂就要傳 MEM_RESERVE為引數
如果打算預訂一塊區域,並且用很長時間,那我們可能會希望系統從儘可能高的記憶體地址來預訂。這樣可以防止程序地址空間的中間預訂區域,從而避免可能會引起記憶體碎片。
fdwProtect:給區域指定保護屬性。
如果應用程式在非統一記憶體訪問的機器上執行,我們可以呼叫VirtualAllocExNuma函式來強制系統在某個節點的實體記憶體中分配一部分虛擬記憶體
15.2 給區域調撥物理儲存器、
在預了區域之後,我們還需要給給區域調撥物理儲存器,這樣才能訪問其中的記憶體地址。系統會從頁交換檔案中來調撥物理儲存器給區域。在調撥物理儲存器時,起始地址始終都是頁面大小的整數倍,整個大小也是頁面大小的整數倍
調撥物理儲存器,我們必須再次呼叫VirtualAlloc。但這次我們會傳 MEM_COMMIT來作為第二個引數fdwAllocationType的值
在已預訂的區域中,我們“必須”告訴VirtualAlloc要調撥多少物理儲存器給哪裡。這是通過pvAddress和dwSize來指定的。前者表示想要調撥物理儲存器給哪個記憶體地址,後來表示物理儲存器的數量。以位元組為單位。
15.3 同時預訂和調撥物理儲存器
PVOID pvMem = VirtualAlloc(NULL,99*1024,MEM_RESERVE | MEM_COMMIT,PAGE_READWRITE)
15.4 何時調撥物理儲存器
何時調撥物理儲存器是一個問題,例如我們正在實現一個電子表格的程式,它支援200行,256。如果一個單元格是一個CELLDATA結構的話,如果這一個結構頕128位元組,那麼這個二維陣列需要 200 * 256 * 128 的位元組的物理儲存器。那麼如果一開啟程式就去調撥,明顯會頕用大量的物理儲存器。尤其是因為使用者經常只用一兩個單元格的時候,那麼這種調撥方法明顯是浪費
虛擬記憶體技術為我們提供了方案:
1.預訂一塊足夠大的區域來容納CELLDATA陣列。只預訂根本不會消耗物理儲存器
2.當用戶某個單元格輸入資料時,首先確定CELLDATA的記憶體地址。當然沒有調撥,會引發訪問違規
3.給第二步中的記憶體地址調撥足夠的物理儲存器。
4.設定CELLDATA結構成員的內容
虛擬記憶體技術存在一個問題是:我們必須確定什麼時候需要調撥物理儲存器。
有以下四種方法來確定是否需要給區域中的某一部分調撥物理儲存器
1.總是嘗試調撥,這種該當的缺點是每次對CELLDATA進行修改時,要多呼叫一次 VirtualAlloc。
2.使用VirtualQuery來判斷是否給CELLDATA調撥過了。這種方法其實比第一種方法還糟糕:由於額外呼叫了VirtuaQuery函式,它不但增加了程式的大小,而且還降低了程式的效能
3.記錄哪些被調撥過。這樣來實時調撥
4.使用結構化異常處理------最佳方案。讓程式底圖方法記憶體異常的時候,調撥。
15.5 撤銷缺氧物理儲存器及釋放區域
要撤銷調撥給區域的物理儲存器,或是釋放地址空間中的一整塊區域,可以呼叫
VirtualFree函式。
如果程序不再需要訪問區域中的物理儲存器,那麼我們只需要呼叫VirtaulFree一次,就能夠釋放整個區域以及調撥給該區域的物理儲存。我們必須傳MEM_RELEASE給fdwFreeType來告訴系統撤銷調撥給該區域的所有物理儲存器,並釋放區域。
如果想要撤銷區域的一部分,我們只需要給pvAddress傳地址,然後dwSize傳大小,fdwFreeType傳MEM_DECOMMIT就行
15.5.1何時撤銷調撥物理儲存器
15.6 改變保護屬性

15.7 改變物理儲存器的內容

當我們修改物理儲存頁中的內容時,系統會盡量把改動保持在記憶體中。但是,當應用程式在執行的時候,系統可能需要從exe檔案,或dll檔案或頁交換檔案中載入新的頁面到記憶體裡。為了滿足最近的載入請求,系統會在記憶體查詢可用的頁面,如果找到的頁面已經被修改過,那麼系統還必須將它們換出到頁交換檔案中。

Windows還提供了一種特性,一路使得應用程式能夠提高中自身的效能-----這項特性就是重置物理儲存器。重置物理儲存器的意思是,我們告訴系統一個或幾個物理儲存頁中的資料沒有被修改過。如果系統正在查詢一頁閒置記憶體並且找到了一個修改過的頁面,那麼系統必須把該記憶體寫入到頁交換檔案中。這個操作比較慢,會影響效能。對大多數應用程式來說,我們都希望系統把修改後的頁面儲存到頁交換檔案中。

但是,有些應用程式只需要在一小段時間內使用儲存器,之後也不需要保留儲存器中的內容。為了提高效能,應用程式可以告訴系統不要在頁交換檔案中儲存指定儲存器。這基本上是應用程式用來告訴系統一個頁面未被修改過的一種方法。因此,如果系統決定將一個記憶體頁挪作他用,好麼它不會將頁面內容儲存到頁交換檔案中,這樣提高了效能。為了重置儲存器,應用程式應該呼叫VirtualAlloc函式,並在第三個引數中傳MEM_RESET標誌。

15.8 地址視窗擴充套件

地址視窗擴充套件特性:

1.允許應用程式以一種特殊的方式分配記憶體,作業系統保證不會將以這種方式分配的記憶體換出到磁碟上

2.允許應用程式訪問比程序地址空間還要多的記憶體。

基本上 AWE提供了一種方式,可以讓應用程式分配一塊或多塊記憶體。當一開始分配時,在程序的地址空間中是看不見這些記憶體塊的應用程式然後預訂地址空間區域,這就是地址視窗。應用程式然後呼叫一個函式,每呼叫一次把一塊記憶體指定到該地址視窗。把記憶體塊指定到地址視窗是非常快的。

//1.預訂1MB地址空間
    ULONG_PTR ulRAMBytes = 1024 * 1024;
    PVOID pvWindow = VirtualAlloc(NULL,ulRAMBytes,
        MEM_RESERVE | MEM_PHYSICAL ,PAGE_READWRITE);

    //2.得到當前平臺的一個頁面的大小
    SYSTEM_INFO sinf;
    GetSystemInfo(&sinf);

    //3.計算需要多少記憶體頁
    ULONG_PTR ulRAMPages = (ulRAMBytes + sinf.dwPageSize - 1) / sinf.dwPageSize;

    //4.分配記憶體頁陣列、
    ULONG_PTR * aRAMPages = (ULONG_PTR * ) new ULONG_PTR[ulRAMPages];

    //5.分配物理儲存器
    AllocateUserPhysicalPages(GetCurrentProcess(),
        &ulRAMPages,
        aRAMPages);

    //6. 指定記憶體塊指定給地址視窗 
    MapUserPhysicalPages(pvWindow,
        ulRAMPages,
        aRAMPages);

    //7.使用記憶體...

    //8.釋放記憶體頁塊
    FreeUserPhysicalPages(GetCurrentProcess(),
        &ulRAMPages,
        aRAMPages);

    VirtualFree(pvWindow,0,MEM_RELEASE);
    delete [] aRAMPages;

相關推薦

windows 核心程式設計應用程式使用虛擬記憶體

Microsoft Windows 提供了以下三種機制來對記憶體進行操控: 虛擬記憶體 最適合用來管理大型物件陣列或大型結構陣列 記憶體對映檔案 最適合用來管理大型資料流(通常是檔案),以及在同一臺機器上執行多個程序之間共享資料 堆 最適合管理大量小型物件 本篇只討論第一

Windows核心程式設計執行緒

執行緒組成兩部分: 1. 一個執行緒的核心物件,作業系統用它管理執行緒。 2. 一個執行緒棧,用於維護執行緒執行時所需的所有函式引數和區域性變數。 何時建立執行緒?舉例: 作業系統的Windows Indexing Services,磁碟碎片整理程式等,都是使用多執行緒進行效能優化的

windows核心程式設計程序

什麼是程序? 程序是一個正在執行程式的例項。由兩部分組成:一個核心物件,用於管理程序以及一個地址空間,包含所有可執行檔案或DLL模組的程式碼和資料,此外還包含動態記憶體分配。 在分析程序之前,先看下windows程式是如何建立的? Windows應用程式分為CUI和GUI程式,即控

vc++程式設計程式加入網址連結

       在vc++對話方塊程式設計中,我們處於某種需要(介紹自己的軟體或者自己的部落格)可以在對話方塊上增加一個網址連結,使用者只要一點選,就進入了相應的網頁,我在此演示下如何完成。   1 開啟編譯器,我們新建一個基於對話方塊的工程(其他形式的也可以),我們以對話

Windows核心程式設計:分頁記憶體與非分頁記憶體

Windows規定有些虛擬記憶體可以交換到檔案中,這類記憶體被稱為分頁記憶體 有些虛擬記憶體永遠不會交換到檔案中,這些記憶體叫非分頁記憶體 #define PAGEDCODE code_seg(“PAGE”);//分頁記憶體 #define LOCKEDCODE c

Windows核心程式設計郵槽實現程序間通訊

    郵槽是Windows系統提供的一種單向通訊的機制。即程序中的一方只能寫入或讀取資料,而另一方則只能讀取或寫入資料。通過郵槽,使用者可以實現一對多或跨網路的程序之間的通訊。但是,郵槽能傳輸的資料

windows核心程式設計使用執行緒APC回撥安全退出多個等待執行緒

前言 程式開發中經常遇到需要這些情況:輔助執行緒正在等待核心物件的觸發,主執行緒需要強制終止輔助執行緒。我們常常做的就是使用:TerminateThread來強制終止執行緒。這樣做當然是不太好的,強制

Windows核心程式設計多程序概述

三、與程序相關的API 4、獲取程序的可執行檔案或DLL對應的控制代碼               HMODULE GetModuleHandle(                       PCTSTR pszModule); //模組名稱               注:當引數傳NULL時獲取的是程序的

Windows核心基礎(二):虛擬記憶體空間佈局

32位Windows作業系統支援32位定址,因此2的32次方就等於4GB,每個程式在執行時都會被對映進4GB空間的記憶體空間,這4GB空間不全是使用者可以使用的,其中0x7fffffff-0xffffffff是2GB的核心空間,這部分用來儲存核心的資料,使用者程式是無法直接訪問的。

應用程式使用虛擬記憶體——Windows核心程式設計學習手札十五

在應用程式中使用虛擬記憶體 ——Windows核心程式設計學習手札之十五 Windows提供了3種進行記憶體管理的方法: 1)虛擬記憶體,最適合用來管理大量物件或結構陣列; 2)記憶體對映檔案,最適合用

Windows 核心程式設計研究系列二 讀取指定實體記憶體地址的內容

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

基於Visual C++Windows核心程式設計程式碼分析(1)實現裝置管理器列舉裝置

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

基於visual c++windows核心程式設計程式碼分析(21)獲取和設定環境變數

環境變數是一個具有特定名字的物件,它包含了一個或者多個應用程式所將使用到的資訊。例如path,當要求系統執行一個程式而沒有告訴它程式所在的完整路徑時,系統除了在當前目錄下面尋找此程式外,還應到path中指定的路徑去找。使用者通過設定環境變數,來更好的執行程序。 環境變數一

Windows核心程式設計基礎篇使用自旋鎖

   連結串列之類的結構總是涉及多執行緒,這時候就要用到使用鎖。     當然在處理臨界區的時候,這個是必須要考慮的呀。    在驅動開發的程式碼中,大多是在於多執行緒執行環境的。     下面將介紹使用簡單的自旋鎖。     如下的程式碼將初始化一個自旋鎖: KSPIN

linux 核心程式設計proc虛擬檔案系統

在 Linux 系統中,/proc 檔案系統十分有用,它被用於核心向用戶匯出資訊。/proc 檔案系統是一個虛擬檔案系統,通過它可以使用一種新的方法在 Linux 核心空間和使用者空間之間進行通訊。在/proc 檔案系統中,我們可以將對虛擬檔案的讀寫作為與核心中實體進行

Windows核心程式設計》筆記-虛擬記憶體記憶體

1、 32位系統支援的最大地址空間4GB(2^32),為何是4GB而不是4Gb呢?因為最小儲存單元是Byte(這個是由系統決定,有些系統可能不一樣)。 由上可知:記憶體條容量大於4GB時,32位系統可能會浪費記憶體。記憶體條小於4GB時,32位系統支援的最大定址空間由記憶體

應用程式使用虛擬記憶體(VirtualAlloc VirtualFree)

此虛擬記憶體非彼虛擬記憶體,此虛擬記憶體實際上指的是虛擬地址空間 LPVOID VirtualAlloc{ LPVOID lpAddress, // 要分配的記憶體區域的地址 DWORD dwSi

windows核心程式設計--探索虛擬記憶體

-系統資訊 VOID GetSystemInfo(LPSYSTEM_INFO psi); dwPageSize:頁面大小 dwAllocationGranularity:分配粒度 -虛擬記憶體狀

基於visual c++windows核心程式設計程式碼分析(18)遠端程式碼注入執行

我們進行系統級別的安全監控的時候,防範木馬的時候,經常需要進行遠端程式碼注入執行。執行步驟如下1. 提升程序許可權,如果許可權不夠的話,很容易造成 OpenProcess 失敗;2. 確定你的宿主程序,即你所要注入程式碼的程序,這個其實很好辦,你要是不想你的木馬或者病毒被別個

Windows高階程式設計執行緒與核心物件的同步

使用者方式同步的優點是它的同步速度非常快。如果強調執行緒的執行速度,那麼首先應該確定使用者方式的執行緒同步機制是否適合需要。使用者方式執行緒同步機制的侷限性:1、互鎖函式家族只能在單值上執行2、關鍵程式碼段只能對單個程序中的執行緒實施同步3、關鍵程式碼段容易陷入死鎖狀態,因為