1. 虛擬位址空間的分區
◆ null 指標賦值分區(從0x00000000~0x0000FFFF的閉區間).保留該分區的目的是為了協助程式員捕獲對null 指標的賦值.如果進程中的線程試圖讀取擷取寫入位於這個分區內的記憶體位址,就會引發訪問違規.(註明:我們在運行代碼的時候常常會碰到一種情況就是,我們指標還沒有分配指向的空間就開始進行指向操作.這樣我們的程式如果是在Debug模式下,程式會down在一個地方,我們可以查看它的記憶體,發現其為:0xcccccccc.擷取有時為0x00000000).
◆ 使用者模式分區(根據系統的位元不同而有差異).這塊地區進程無法通過指標來讀取,寫入或者以任何方式,訪問駐留在這一分區中其他進程的資料.進程的大部分資料都儲存在這一分區(像.exe和DLL,另外系統還會把該進程可以訪問的所有記憶體對應檔映射到這一分區).
預設情況下當運行一個64位應用程式時,系統就會保留使用者模式地址空間中位於地址0x000000008000000,這也就保證了所有記憶體都分配自64位地址空間中最底部的2GB.(這就是空間沙箱).
我們可以利用兩個函數來分配和釋放地區:VirtualAlloc和VirtualFree.
2. 當使用者執行一個應用程式時,系統會開啟該應用程式對應的exe檔案並計算出應用程式的代碼和資料的大小.然後系統會預定一塊地址空間,並註明與該地區先關聯的實體儲存體器就是.exe檔案本身.這樣就大大提高了效能.
記憶體對應檔:當把一個程式位於硬碟上的檔案映像(即exe檔案)用作地址空間地區對應的實體儲存體器時,我們稱這個檔案映像為記憶體對應檔.
3. 頁面保護屬性工作表
保護屬性 |
描述 |
PAGE_NOACCESS |
試圖讀取頁面、寫入頁面或執行頁面中的代碼將引發訪問違規 |
PAGE_READONLY |
試圖寫入頁面或者執行頁面中的代碼將引發訪問違規 |
PAGE_READWRITE |
試圖執行頁面中的代碼將引發訪問違規 |
PAGE_EXECUTE |
試圖讀取頁面或者寫入頁面將引發訪問違規 |
PAGE_EXECUTE_READ |
試圖寫入頁面將引發訪問違規 |
PAGE_EXECUTE_READWRITE |
對頁面執行任何操作都不會引發訪問違規 |
PATE_WRITECOPY |
試圖執行頁面中的代碼將引發訪問違規,試圖寫入頁面將使系統為進程單獨建立一份該頁面的私人副本(以頁分頁檔為備份存放區器) |
PAGE_EXECUTE_WRITECOPY |
對頁面執行任何操作都不會引發訪問違規.試圖寫入頁面將使系統為單獨單獨建立一份改頁面的私人副本(以頁分頁檔為備份存放區器) |
如果啟用了資料執行防止(DEP),那麼只有對那些真正需要執行代碼的記憶體地區,作業系統才會使用PAGE_EXECUTE_*保護屬性.其他保護屬性用於只應該存放資料的記憶體地區.(比如線程棧和應用程式堆).如果CPU要執行沒有PAGE_EXECUTE_*保護屬的代碼,將會引發異常.
有時系統為了提高效率,經常需要將兩個或者兩個以上的進程共用同一儲存空間.為了避免不必要的的錯誤,系統會給共用的儲存頁指定寫時複製屬性.當系統把一個exe或dll映射到一個地址空間的時候會計算有多少可寫頁,然後系統從頁分頁檔分配儲存空間來容納這些可寫頁面.如果兄台那個試圖寫入一個共用頁面時將執行以下步驟:
1). 系統在記憶體中找到一個閑置頁面.
2). 系統把線程想要修改的頁面內容複寫到在第一步找到的閑置頁面.注意此時系統不會對原來的頁面進行任何修改.
3). 更新進程頁面表.
另外在預定地址或者調撥實體儲存體器時,不能使用PAGE_WRITECOPY或者PAGE_EXECUTE_WRITECOPY保護屬性.這樣做會導致VirtualAlloc失敗.
其他的訪問屬性標誌:
PAGE_NOCACHE:用來禁止對已調撥的頁面進行緩衝.該標誌存在的主要目的是為了讓需要操控記憶體緩衝區的驅動程式開發人員使用,而不建議其他人使用.
PAGE_WRITECOMBINE:也是供驅動程式開發人員使用.它允許把對單個裝置的多次寫操作組合在一起,以提高效率.
PAGE_GUARD:使應用程式能夠在頁面中的任何一個位元組被寫入時得到通知.
4. 資料的對齊:只有當訪問已對齊的資料時,CPU的執行效率才是最高的.把資料的地址模除資料的大小,如果結果為0,表示資料時對齊的.
X86和AMD CPU中,是靠CPU來自動執行必要的操作來訪問錯位元據.而IA-64 CPU不能自動處理,它是通過通知作業系統,讓其來進行處理.正常情況下,windows會自動將資料錯位的錯誤轉換成一個EXCEPTION_DATATYPE_MISALIGNMENT異常.我們可利用SetErrorMode,讓作業系統為進程中的所有線程自動修正資料錯位的錯誤.
UINT SetErrorMode(UINT fuErrorMode);
這裡我們應該設定SEM_NOALIGNMENTFAULTEXCEPT標誌.只要設定了該標誌系統會自動修正對錯位元據的訪問.一旦改變這個標誌,那麼就無法在在進程的生命週期內再次改變它.
在IA-64 CPU版本的C/C++編譯器中,當通過一個帶__unaligned修飾符的指標訪問資料時編譯器會認為資料未經過對齊,並產生額外的CPU指令以訪問資料.(需要注意的是x86並不支援這個關鍵字)