再談計(jì)算機(jī)內(nèi)存訪問_第1頁
再談計(jì)算機(jī)內(nèi)存訪問_第2頁
再談計(jì)算機(jī)內(nèi)存訪問_第3頁
再談計(jì)算機(jī)內(nèi)存訪問_第4頁
再談計(jì)算機(jī)內(nèi)存訪問_第5頁
已閱讀5頁,還剩124頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

第1章再談計(jì)算機(jī)內(nèi)存訪問

1.1引言本章提要·

內(nèi)存管理概述·

虛擬內(nèi)存訪問的操作函數(shù)·

文件的內(nèi)存映射·

深入認(rèn)識C語言指針的真正含義1.1

引言要成為一個(gè)程序編寫高手,不僅需要熟悉各種計(jì)算機(jī)語言,而且還需要懂得計(jì)算機(jī)是如何工作的。雖然不必對計(jì)算機(jī)的各個(gè)部件了解得十分清楚,但至少需要懂得計(jì)算機(jī)操作系統(tǒng)對內(nèi)存是如何管理的。只有這樣,才能編寫出計(jì)算機(jī)內(nèi)核級的程序來。本書的以后章節(jié)會涉及到內(nèi)存管理的一些操作函數(shù),因此,有必要先介紹這方面的內(nèi)容。其實(shí),有關(guān)計(jì)算機(jī)內(nèi)存管理的書籍已經(jīng)很多了,為了加深讀者對這些內(nèi)存管理函數(shù)使用方法的進(jìn)一步認(rèn)識,這里重復(fù)談?wù)摯嗽掝}。計(jì)算機(jī)是由各種電子器件組成的,其核心部分是中央處理器,它的英文名字叫CPU,也常被稱為微處理器。微處理器有各種各樣的型號,如80386、80486、80586(Pentium,這個(gè)詞是“第五代”的意思)和80686(Pentium2)等。通??梢酝ㄟ^向CPU送出指令對計(jì)算機(jī)內(nèi)存進(jìn)行訪問。在計(jì)算機(jī)硬件發(fā)展的同時(shí),PC計(jì)算機(jī)的操作系統(tǒng)也在不斷更新。20世紀(jì)90年代以前,個(gè)人電腦的操作系統(tǒng)是DOS,此后Windows操作系統(tǒng)逐步成為主導(dǎo)。DOS和Windows操作系統(tǒng)對計(jì)算機(jī)內(nèi)存管理的方式不同,前者主要采用實(shí)模式管理,而后者主要采用保護(hù)模式管理。本章首先介紹保護(hù)模式下的分頁機(jī)制,然后介紹如何進(jìn)行內(nèi)存訪問的操作。

內(nèi)存管理概述內(nèi)存管理是操作系統(tǒng)最重要的一部分,它決定了操作系統(tǒng)的性能。為了說明如何進(jìn)行內(nèi)存訪問的操作,有必要先介紹有關(guān)內(nèi)存管理的一些術(shù)語及背景。

虛擬內(nèi)存所謂虛擬內(nèi)存就是用硬盤空間來彌補(bǔ)計(jì)算機(jī)物理內(nèi)存不足的技術(shù)。Windows操作系統(tǒng)用虛擬內(nèi)存來動態(tài)管理運(yùn)行時(shí)的交換文件。為了提供比實(shí)際物理內(nèi)存還多的內(nèi)存容量,Windows操作系統(tǒng)占用了硬盤上的一部分空間作為虛擬內(nèi)存。當(dāng)CPU有要求時(shí),首先會讀取內(nèi)存中的資料。當(dāng)內(nèi)存容量不夠用時(shí),Windows就會將需要暫時(shí)存儲的數(shù)據(jù)寫入硬盤。所以,計(jì)算機(jī)的內(nèi)存大小等于實(shí)際物理內(nèi)存容量加上“分頁文件”(就是交換文件)的大小。Windows98中分頁文件名采用Win386.swp形式,而Windows2K/XP/2003中采用pagefie.sys,默認(rèn)位于系統(tǒng)分區(qū)的根目錄下,具有隱藏屬性。如果需要的話,“分頁文件”會動用硬盤上所有可以使用的空間。安裝好Windows以后,系統(tǒng)采用默認(rèn)的設(shè)置自動處理虛擬內(nèi)存,為了優(yōu)化系統(tǒng)的

工作性能,根據(jù)Windows操作系統(tǒng)中虛擬內(nèi)存的設(shè)置方法,可以自己動手設(shè)置內(nèi)存管理參數(shù)。

CPU工作模式計(jì)算機(jī)系統(tǒng)有不同的工作模式,在不同的模式下,CPU的尋址方式是不一樣的,通常見到的CPU工作模式如下所述。實(shí)模式是為了Pentium處理器與8086/8088兼容而設(shè)置的。8086和8088只能工作于實(shí)模式,而80286及以上的處理器可工作于實(shí)模式或者保護(hù)模式下。實(shí)模式操作方式只允許微處理器尋址第一個(gè)1MB的存儲空間,從0x00000~0xFFFFF。在實(shí)模式下的存儲器尋址是段地址+偏移地址。例如段寄存器的內(nèi)容是0x1000,則它尋址開始于0x10000的段,偏移量大小從0x0000~0xFFFF,即偏移量的空間大小是216=64KB。保護(hù)地址模式又稱為虛擬地址存儲管理方式。保護(hù)模式下主要有兩種特征。(1)內(nèi)存分段管理在保護(hù)模式下,各個(gè)16位的段寄存器里面放置的是選擇符。各項(xiàng)任務(wù)共享的內(nèi)存空間由全局選擇符來索引;而某個(gè)任務(wù)獨(dú)立使用的內(nèi)存空間由局部選擇符來索引。由選擇符的高13位作為偏移量,再以CPU內(nèi)部事先初始化好的GDTR(全局描述符表寄存器)中的32位基地址為基,可以獲得相應(yīng)的描述符。由描述符中的線性地址決定段的基地址。再利用指令(或其他方式)給出的偏移量,便可以得到線性地址,即線性地址=段線性基地址+偏移量保護(hù)模式采用上面介紹的分段管理,可以實(shí)現(xiàn)的存儲器尋址范圍為4GB,通常把通過段變換獲得的地址稱為線性地址。這種線性地址是同32位物理地址對應(yīng)的,為了獲得更大的尋址范圍,還可以對線性地址實(shí)行分頁管理。在保護(hù)模式下,處理器通過CRO控制寄存器的PG(page)位進(jìn)行管理,當(dāng)PG=0時(shí),由段變換獲得的線性地址可直接作為物理地址使用;若PG=1,則進(jìn)一步進(jìn)行頁變換。(2)內(nèi)存分頁管理分頁管理的基本思想是將內(nèi)存分為大小固定為4KB或者1MB的若干頁,通過一定機(jī)制對內(nèi)存進(jìn)行管理。與前面的分段管理類似,程序或數(shù)據(jù)將根據(jù)其長度分配若干頁。為了進(jìn)行頁面管理,在分頁管理機(jī)制中采用了頁表、頁目錄對線性地址作頁變換。

邏輯、線性和物理地址在保護(hù)地址模式下,經(jīng)常遇到三種地址:邏輯地址(ogicaAddress)、線性地址(inearAddress)和物理地址(PhysicaAddress)。CPU通過分段機(jī)制將邏輯地址轉(zhuǎn)換為線性地址,再通過分頁機(jī)制將線性地址轉(zhuǎn)換為物理地址。(1)邏輯地址這是內(nèi)存地址的精確描述,通常表示為十六進(jìn)制:xxxx:YYYYYYYY,這里xxxx為seector(選擇器),而YYYYYYYY是針對seector所選擇的段地址的線性偏移量。除了指定xxxx的具體數(shù)值外,還可使用具體的段寄存器的名字來替代,如CS(代碼段),DS(數(shù)據(jù)段),ES(擴(kuò)展段),FS(附加數(shù)據(jù)段#1),GS(附加數(shù)據(jù)段#2)和SS(堆棧段)。這些符號都來自舊的“段:偏移量”風(fēng)格,在8086實(shí)模式下使用此種方式來指定“farpointers”(遠(yuǎn)指針)。(2)線性地址線性地址是邏輯地址到物理地址變換之間的中間層,是處理器可尋址的內(nèi)存空間(稱為線性地址空間)中的地址。程序代碼會產(chǎn)生邏輯地址,或者說是段中的偏移地址,加上相應(yīng)段的基地址就生成了一個(gè)線性地址。如果啟用了分頁機(jī)制,那么線性地址可以再經(jīng)變換以產(chǎn)生一個(gè)物理地址。若沒有啟用分頁機(jī)制,那么線性地址直接就是物理地址。不過,在開啟分頁功能之后,一個(gè)線性地址可能沒有相對映的物理地址,因?yàn)樗鶎?yīng)的內(nèi)存可能被交換到硬盤中。32位線性地址可用于定位4GB存儲單元。(3)物理地址所謂物理地址,就是指系統(tǒng)內(nèi)存的真正地址。對于32位的操作系統(tǒng),它的范圍為0x00000000~0xFFFFFFFF,共有4GB。只有當(dāng)CPU工作于分頁模式時(shí),此種類型的地址才會變得非?!坝腥ぁ?。本質(zhì)上,一個(gè)物理地址是CPU插腳上可測量的電壓。操作系統(tǒng)通過設(shè)立頁表將線性地址映射為物理地址。Windows2K/XP所用頁表布局的某些屬性對于調(diào)試軟件開發(fā)人員非常有用。

存儲器分頁管理機(jī)制程序代碼和數(shù)據(jù)必須駐留在內(nèi)存中才能得以運(yùn)行,然而系統(tǒng)內(nèi)存量很有限,往往不能容納一個(gè)完整程序的所有代碼和數(shù)據(jù),特別是在多任務(wù)系統(tǒng)中,如Windows,可能需要同時(shí)打開多個(gè)執(zhí)行程序,如畫圖程序,瀏覽器等,想讓內(nèi)存駐留所有這些程序顯然不大可能,因此首先能想到的就是將程序分割成小部分,只讓當(dāng)前系統(tǒng)運(yùn)行它所有需要的那部分留在內(nèi)存,其他部分都留在硬盤(虛擬內(nèi)存)。當(dāng)系統(tǒng)處理完當(dāng)前任務(wù)片段后,再從外存中調(diào)入下一個(gè)待運(yùn)行的任務(wù)片段。于是,存儲器分頁管理機(jī)制隨之而被發(fā)明。如前所述,在保護(hù)模式下,控制寄存器CR0中的最高位PG位控制分頁管理機(jī)制是否生效。如果PG=1,分頁機(jī)制生效,把線性地址轉(zhuǎn)換為物理地址。如果PG=0,分頁機(jī)制無效,線性地址就直接作為物理地址。必須注意,只有在保護(hù)方式下分頁機(jī)制才可能生效。只有在保證使PE位為1的前提下,才能夠使PG位為1,否則將引起通用保護(hù)故障。分頁機(jī)制把線性地址空間和物理地址空間分別劃分為大小相同的塊。這樣的塊稱為頁。通過在線性地址空間的頁與物理地址空間的頁之間建立映射,分頁機(jī)制可以實(shí)現(xiàn)線性地址到物理地址的轉(zhuǎn)換。線性地址空間的頁與物理地址空間的頁之間的映射可根據(jù)需要來確定。線性地址空間的任何一頁,可以映射為物理地址空間中的任何一頁。

線性地址到物理地址的轉(zhuǎn)換線性地址空間的頁到物理地址空間的頁之間的映射用表來描述。目前所見到的有4KB和1MB大小的物理分頁,對于4KB頁面的分頁,線性地址到物理地址的轉(zhuǎn)換過程如圖1.1所示。對于1MB頁面分頁,線性地址到物理地址的轉(zhuǎn)換與4KB的基本相似,不同的是線性地址的低22位對應(yīng)一個(gè)物理頁面。對于4KB頁面的線性地址到物理地址的轉(zhuǎn)換示意圖對于4KB頁面分頁,頁映射表的第一級稱為頁目錄表,存儲在一個(gè)物理頁中。頁目錄表共有1024個(gè)頁目錄項(xiàng)(PDE,pagedirectoryentry),其中,每個(gè)PDE為4字節(jié)長,包含對應(yīng)第二級表所在物理地址空間頁的頁碼。頁映射表的第二級稱為頁表,每張頁表也被存儲在一個(gè)物理頁中。每張頁表有1024個(gè)頁表項(xiàng)(PTE,pagetabeentry),每個(gè)PTE為4字節(jié)長,其中PTE的低12位用來存放諸如“頁是否存在于內(nèi)存”或“頁的權(quán)限”等信息。一個(gè)線性地址大小為4個(gè)字節(jié)(32bit),包含著找到物理地址的信息,分為3個(gè)部分:第22位到第31位這10位(最高10位)是頁目錄中的索引,第12位到第21位這10位是頁表中的索引,第0位到第11位這12位(低12位)是頁內(nèi)偏移。在把一個(gè)線性地址轉(zhuǎn)換成物理地址時(shí),CPU首先根據(jù)CR3中的值,找到頁目錄所在的物理頁。然后根據(jù)線性地址的第22位到第31位這10位(最高的10bit)的值作為索引,找到相應(yīng)的PDE,其中含有這個(gè)虛擬地址所對應(yīng)頁表的物理地址。有了頁表的物理地址,再把虛擬地址的第12位到第21位這10位的值作為索引,找到該頁表中相應(yīng)的PTE,其中就有這個(gè)虛擬地址所對應(yīng)物理頁的物理地址。最后用線性地址的最低12位,也就是頁內(nèi)偏移,加上這個(gè)物理頁的物理地址,就得到了該線性地址所對應(yīng)的物理地址。

虛擬內(nèi)存訪問每個(gè)進(jìn)程都擁有自己的虛擬地址空間,那么怎樣才能訪問這個(gè)空間呢?這就需要用到WindowsAPI函數(shù)。這些函數(shù)直接與編寫程序相關(guān),因而更受軟件工程師的關(guān)注。有關(guān)這方面的函數(shù)較多,這里介紹幾個(gè)重要的函數(shù)。

獲取系統(tǒng)信息在一個(gè)程序中不能直接應(yīng)用某個(gè)系統(tǒng)的設(shè)備參數(shù),否則將不利于程序的移植。因此,如果確實(shí)需要用到這樣的設(shè)備參數(shù),則需要一個(gè)系統(tǒng)信息函數(shù)來獲得。VC++編譯器所提供這樣的函數(shù)為GetSystemInfo()。該函數(shù)需要一個(gè)指向SYSTEM_INFO結(jié)構(gòu)的指針作為參數(shù)。其原型表示為:

voidGetSystemInfo(PSYSTEM_INFOpSystemInfo);其中pSystemInfo返回PSYSTEM_INFO結(jié)構(gòu)的地址,用于裝載適當(dāng)?shù)南到y(tǒng)信息,這個(gè)結(jié)構(gòu)體定義為:typedefstruct_SYSTEM_INFO{

union{

DWORDdwOemId;

struct{

WORDwProcessorArchitecture;

WORDwReserved;

};

};

DWORD

dwPageSize;

PVOID

pMinimumAppicationAddress;

PVOID

pMaximumAppicationAddress;

DWORD_PTR

dwActiveProcessorMask;

DWORD

dwNumberOfProcessors;

DWORD

dwProcessorType;

DWORD

dwAocationGranuarity;

WORD

wProcessoreve;

WORD

wProcessorRevision;}SYSTEM_INFO;其中參數(shù)含義如下所述。dwOemId:是一個(gè)過時(shí)選項(xiàng),用于與WindowsNT3.5以及以前的版本兼容。wProcessorArchitecture:指明處理的結(jié)構(gòu),如Inte、Apha、Inte64位或Apha

64位。dwPageSize:用于顯示CPU的頁面大小。在x86CPU上,這個(gè)值是4096字節(jié)。在AphaCPU上,這個(gè)值是8192字節(jié)。在IA-64上,這個(gè)值是8192字節(jié)。pMinimumAppicationAddress:用于給出每個(gè)進(jìn)程可用地址空間的最小內(nèi)存地址。在Windows98上,這個(gè)值是0x400000,因?yàn)槊總€(gè)進(jìn)程的地址空間中下面的4MB是不能使用的。在Windows2K/XP上,這個(gè)值是0x10000,因?yàn)槊總€(gè)進(jìn)程的地址空間中開頭的64KB總是空閑的。pMaximumAppicationAddress:用于給出每個(gè)進(jìn)程可用地址空間的最大內(nèi)存地址。在Windows98上,這個(gè)地址是0x7FFFFFFF,因?yàn)楣蚕韮?nèi)存映射文件區(qū)域和共享操作系統(tǒng)代碼包含在上面的2GB分區(qū)中。在WindowsXP上,這個(gè)地址是0x7FFEFFFF。dwActiveProcessorMask:位屏蔽,指明哪個(gè)CPU是活動的。dwNumberOfProcessors:計(jì)算機(jī)中CPU的數(shù)目。dwProcessorType:處理器類型。dwAocationGranuarity:保留的地址空間區(qū)域的分配粒度。wProcessoreve:進(jìn)一步細(xì)分處理器的結(jié)構(gòu)。wProcessorRevision:用于進(jìn)一步細(xì)分處理器的級別。wReserved:保留供將來使用。在以上參數(shù)中只有pMinimumAppicationAddress、pMaximumAppicationAddress、dwPageSize和dwAocationGranuarity與內(nèi)存有關(guān)。

在應(yīng)用程序中使用虛擬內(nèi)存對內(nèi)存分配可以采用不同的方法,常用的方法有:用C/C++語言的內(nèi)存分配函數(shù),例如,用maoc()和free()、new和deete函數(shù)分配和釋放堆內(nèi)存;用Windows傳統(tǒng)的全局或者局部內(nèi)存分配函數(shù),如GobaAoc()和GobaFree();用Win32的堆分配函數(shù),如HeapAoc()和HeapFree();用Win32的虛擬內(nèi)存分配函數(shù),如VirtuaAoc()和VirtuaFree()。注意,用不同的方法分配內(nèi)存后,要用相對應(yīng)的函數(shù)來釋放所占用的內(nèi)存。這里只介紹Win32的虛擬內(nèi)存分配函數(shù)。在進(jìn)程創(chuàng)建之初并被賦予地址空間時(shí),其虛擬地址空間尚未分配,處于空閑狀態(tài)。這時(shí)地址空間內(nèi)的內(nèi)存是不能使用的,必須通過VirtuaAoc()函數(shù)來分配其中的各個(gè)區(qū)域,對其進(jìn)行保留。VirtuaAoc()函數(shù)原型為:

PVOIDVirtuaAoc(

PVOIDpAddress,

DWORDdwSize,

DWORDfAocationType,

DWORDfProtect

);該函數(shù)用來分配一定范圍的虛擬頁。參數(shù)1指定起始地址;參數(shù)2指定分配內(nèi)存的長度;參數(shù)3指定分配方式,取值MEM_COMMINT或者M(jìn)EM_RESERVE;參數(shù)4指定控制訪問本次分配的內(nèi)存的標(biāo)識,取值為PAGE_READONY、PAGE_READWRITE或者PAGE_NOACCESS。分配完成后,即在進(jìn)程的虛擬地址空間中保留了一個(gè)區(qū)域,可以對此區(qū)域中的內(nèi)存進(jìn)行保護(hù)權(quán)限許可范圍內(nèi)的訪問。當(dāng)不再需要訪問此地址空間區(qū)域時(shí),應(yīng)釋放此區(qū)域,由VirtuaFree()負(fù)責(zé)完成。其函數(shù)原型為:BOOVirtuaFree(

PVOIDpAddress,

DWORDdwSize,

DWORDdwFreeType

);

其中參數(shù)含義如下所述。pAddress:指向待釋放頁面區(qū)域的指針。如果參數(shù)dwFreeType指定了MEM_REEASE,則pAddress必須為頁面區(qū)域保留由VirtuaAoc()所返回的基地址。dwSize:指定了要釋放的地址空間區(qū)域的大小,如果參數(shù)dwFreeType指定了MEM_REEASE標(biāo)志,則將dwSize設(shè)置為0,由系統(tǒng)計(jì)算在特定內(nèi)存地址上的待釋放區(qū)域的大小。dwFreeType:為所執(zhí)行的釋放操作的類型,其可能的取值為MEM_REEASE和MEM_DECOMMIT,其中MEM_REEASE標(biāo)志指明要釋放指定的保留頁面區(qū)域,MEM_DECOMMIT標(biāo)志則對指定的占用頁面區(qū)域進(jìn)行占用的解除。如果VirtuaFree()執(zhí)行完成,將回收全部范圍的已分配頁面,此后如再對這些已釋

放頁面區(qū)域內(nèi)存進(jìn)行訪問將引發(fā)內(nèi)存訪問異常。釋放后的頁面區(qū)域可供系統(tǒng)繼續(xù)分配

使用。

獲取虛存狀態(tài)WindowsAPI函數(shù)GobaMemoryStatus()可用于檢索關(guān)于當(dāng)前內(nèi)存狀態(tài)的動態(tài)信息。在軟件的About對話框中,通常用這個(gè)函數(shù)來獲取系統(tǒng)內(nèi)存的使用情況。其函數(shù)原型為:

voidGobaMemoryStatus(PMEMORYSTATUSpmstMemStat);其中pmstMemStat返回MEMORYSTATUS結(jié)構(gòu)的地址,這個(gè)結(jié)構(gòu)體的定義為:

typedefstructMEMORYSTATUS{

DWORDdwength;

DWORDdwMemoryoad;

DWORDdwTotaPhys;

DWORDdwAvaiPhys;

DWORDdwTotaPageFie;

DWORDdwAvaiPageFie;

DWORDdwTotaVirtua;

DWORDdwAvaiVirtua;

}MEMORYSTATUS,*PMEMORYSTATUS;其中參數(shù)含義如下所述。dwength:MEMORYSTATUS結(jié)構(gòu)大小。dwMemoryoad:已使用內(nèi)存所占的百分比。dwTotaPhys:物理存儲器的總字節(jié)數(shù)。dwAvaiPhys:空閑物理存儲器的字節(jié)數(shù)。dwTotaPageFie:頁文件包含的最大字節(jié)數(shù)。dwAvaiPageFie:用戶模式分區(qū)中空閑內(nèi)存大小。dwTotaVirtua:用戶模式分區(qū)大小。dwAvaiVirtua:表示當(dāng)前進(jìn)程中還剩下的自由區(qū)域的總和。在調(diào)用GobaMemoryStatus()之前,必須將dwength成員初始化為用字節(jié)表示的結(jié)構(gòu)的大小,即一個(gè)MEMORYSTATUS結(jié)構(gòu)的大小。這個(gè)初始化操作使得Microsoft能夠在新版本W(wǎng)indows系統(tǒng)中將新成員添加到這個(gè)結(jié)構(gòu)中,而不會破壞現(xiàn)有的應(yīng)用程序。當(dāng)調(diào)用GobaMemoryStatus()時(shí),它將對該結(jié)構(gòu)的其余成員進(jìn)行初始化并返回。如果某個(gè)應(yīng)用程序在內(nèi)存大于4GB的計(jì)算機(jī)上運(yùn)行,或者合計(jì)交換文件的大小大于4GB,那么可以使用新的GobaMemoryStatusEx()函數(shù)。其函數(shù)的原型為:BOOGobaMemoryStatusEx(MEMORYSTATUSEX

&mst);其中mst返回MEMORYSTATUSEX結(jié)構(gòu)的填充信息,該結(jié)構(gòu)體與原先的MEMORYSTATUS結(jié)構(gòu)基本相同,差別在于新結(jié)構(gòu)的所有成員的大小都是64位寬,因此它的值可以大于4GB。

確定虛擬地址空間的狀態(tài)對內(nèi)存的管理除了對當(dāng)前內(nèi)存的使用狀態(tài)信息進(jìn)行獲取外,還經(jīng)常需要獲取有關(guān)進(jìn)程的虛擬地址空間的狀態(tài)信息。例如,如何得到一個(gè)進(jìn)程已提交的頁面范圍?這就要用到兩個(gè)API函數(shù)VirtuaQuery()或VirtuaQueryEx()來進(jìn)行查詢。這兩個(gè)函數(shù)的功能相似,不同就是VirtuaQuery()只是查詢本進(jìn)程內(nèi)存空間信息,而VirtuaQueryEx()可以查詢指定進(jìn)程的內(nèi)存空間信息。VirtuaQuery()函數(shù)原型如下:DWORDVirtuaQuery(

PVOIDpAddress,

PMEMORY_BASIC_INFORMATIONpBuffer,

DWORDdwength

);VirtuaQueryEx()函數(shù)原型如下:DWORDVirtuaQueryEx(

HANDEhProcess,

PCVOIDpAddress,

PMEMORY_BASIC_INFORMATIONpBuffer,

DWORDdwength

);其中參數(shù)含義如下所述。hProcess:進(jìn)程的句柄。pAddress:想要了解其信息的虛存地址。pBuffer:返回MEMORY_BASIC_INFORMATION結(jié)構(gòu)的地址。dwength:返回的字節(jié)數(shù)。PWEMORY_BASIC_INFORMATION的定義如下:

typedefstruct_MEMORY_BASIC_INFORMATION{

PVOIDBaseAddress;

PVOIDAocationBase;

DWORDAocationProtect;

DWORDRegionSize;

DWORD

DWORDProtect;

DWORDType;}MEMORY_BASIC_INFORMATION,*PMEMORY_BASIC_INFORMATION;其中參數(shù)含義如下所述。BaseAddress:被查詢內(nèi)存塊的基地址。AocationBase:用VirtuaAoc()分配該內(nèi)存時(shí)實(shí)際分配的基地址。AocationProtect:分配該頁面時(shí),頁面的一些屬性,如PAGE_READWRITE、PAGE_EXECUTE等(其他屬性可參考PatformSDK)。RegionSize:從BaseAddress開始,具有相同屬性的頁面的大小。State:頁面的狀態(tài),有3種可能值:MEM_COMMIT、MEM_FREE和MEM_RESERVE,這個(gè)參數(shù)是最重要的,從中可知指定內(nèi)存頁面的狀態(tài)。Protect:頁面的屬性,它可能的取值與AocationProtect相同。Type:指明了該內(nèi)存塊的類型,有3種可能值:MEM_IMAGE、MEM_MAPPED和MEM_PRIVATE。

改變內(nèi)存頁面保護(hù)屬性在進(jìn)行進(jìn)程掛鉤時(shí),經(jīng)常要向內(nèi)存頁中寫入部分代碼,這就需要改變內(nèi)存頁的保護(hù)屬性。有幸的是Win32提供了兩個(gè)API函數(shù)VirtuaProtect()和VirtuaProtectEx(),它們可以對改變內(nèi)存頁保護(hù)。例如,在使用這兩個(gè)函數(shù)時(shí),可以先按PAGE_READWRITE屬性來提交一個(gè)頁的地址,并且立即將數(shù)據(jù)填寫到該頁中,然后再把該頁的屬性改變?yōu)镻AGE_READONY,這樣可以有效地保護(hù)數(shù)據(jù)不被該進(jìn)程中的任何其他線程重寫。在調(diào)用這兩個(gè)函數(shù)之前最好先了解有關(guān)頁面的信息,可以通過VirtuaQuery()來實(shí)現(xiàn)。VirtuaProtect()與VirtuaProtectEx()函數(shù)的區(qū)別在于VirtuaProtect()只適用于本進(jìn)程,而VirtuaProtectEx()可以適用于其他進(jìn)程。VirtuaProtect()函數(shù)原型如下:BOOVirtuaProtect(

PVOIDpvAddress,

DWORDdwSize,

DWORDfNewProtect,

PDWORDpfOdProtect

);

VirtuaProtectEx()函數(shù)原型如下:BOOVirtuaProtectEx(

HANDEhProcess,

PVOIDpvAddress,

DWORDdwSize,

DWORDfNewProtect,

PDWORDpfOdProtect

);其中參數(shù)的含義如下所述。hProcess:要修改內(nèi)存的進(jìn)程句柄。pvAddress:指向內(nèi)存的基地址(它必須位于進(jìn)程的用戶方式分區(qū)中)。dwSize:用于指明想要改變保護(hù)屬性的字節(jié)數(shù)。fNewProtect:代表PAGE_*保護(hù)屬性標(biāo)志中的任何一個(gè)標(biāo)志,但PAGE_WRITECOPY和PAGE_EXECUTE_WRITECOPY這兩個(gè)標(biāo)志除外。pfOdProtect:是DWORD大小的地址,VirtuaProtect()和VirtuaProtectEx()將用原先與pvAddress位置上的字節(jié)相關(guān)的保護(hù)屬性填入該地址。盡管許多應(yīng)用程序并不需要該信息,但是必須為該參數(shù)傳遞一個(gè)有效地址,否則該函數(shù)的運(yùn)行將會失敗。

進(jìn)行一個(gè)進(jìn)程的內(nèi)存讀寫前面已經(jīng)說明了如何獲得一個(gè)進(jìn)程的內(nèi)存屬性、如何分配內(nèi)存和如何改變內(nèi)存頁的保護(hù)屬性,其最終的目的是要對一個(gè)進(jìn)程中內(nèi)存內(nèi)容進(jìn)行讀寫。要完成此工作,需要用到兩個(gè)函數(shù):ReadProcessMemory()和WriteProcessMemory(),這兩個(gè)函數(shù)非常有用。如果知道了一個(gè)進(jìn)程的句柄和內(nèi)存地址,就可以用ReadProcessMemory()函數(shù)來得到該進(jìn)程和該地址中的內(nèi)容,此函數(shù)的原型為:BOOReadProcessMemory(

HANDEhProcess,

PCVOIDpBaseAddress,

PVOIDpBuffer,

DWORDnSize,

PDWORDpNumberOfBytesRead

);其中hProcess為要讀入的進(jìn)程句柄,pBaseAddress為讀內(nèi)存的起始地址,pBuffer為讀入數(shù)據(jù)的地址,nSize為要讀入的字節(jié)數(shù),pNumberOfBytesRead為實(shí)際讀入的字

節(jié)數(shù)。同樣,如果知道了一個(gè)進(jìn)程的句柄和內(nèi)存地址,可以用WriteProcessMemory()函數(shù)向該進(jìn)程和該地址中寫入新的內(nèi)容,這個(gè)函數(shù)的原型為:BOOWriteProcessMemory(

HANDEhProcess,

PVOIDpBaseAddress,

PVOIDpBuffer,

DWORDnSize,

PDWORDpNumberOfBytesWritten

);其中參數(shù)hProcess為要寫入的進(jìn)程句柄,pBaseAddress為寫內(nèi)存的起始地址,pBuffer為寫入數(shù)據(jù)的地址,nSize為要寫入的字節(jié)數(shù),pNumberOfBytesWritten為實(shí)際寫入的字節(jié)數(shù)。

文件的內(nèi)存映射文件的內(nèi)存映射的主要用途有兩方面,第一是用來在多個(gè)進(jìn)程之間共享數(shù)據(jù),第二是直接用內(nèi)存映射文件來訪問磁盤上的數(shù)據(jù)文件,無需再進(jìn)行文件的I/0操作。進(jìn)程間共享數(shù)據(jù)有很多種方法,稍后將對這些技術(shù)進(jìn)行介紹。內(nèi)存映射文件的使用可以分為以下三步:(1)用CreateFieMapping()創(chuàng)建一個(gè)文件映射內(nèi)核對象;(2)用MapViewOfFie()將文件數(shù)據(jù)映射到進(jìn)程的地址空間;(3)用UnmapViewOfFie()從進(jìn)程地址空間解除這個(gè)映射。

內(nèi)存映射API函數(shù)在進(jìn)行內(nèi)存映射文件時(shí),首先要用到的是CreateFieMapping()函數(shù),其原型為:

HANDECreateFieMapping(

HANDEhFie,

PSECURITY_ATTRIBUTESpFieMappingAttributes,

DWORDfProtect,

DWORDdwMaximumSizeHigh,

DWORDdwMaximumSizeow,

PCTSTRpName

);

其中參數(shù)含義如下所述。hFie:指定待映射到進(jìn)程地址空間的文件句柄,例如,可以由CreateFie()函數(shù)的返回值獲取該句柄。如果需要?jiǎng)?chuàng)建一個(gè)與文件無關(guān)的內(nèi)存映射,可以將它設(shè)置成為0xFFFFFFFF(INVAID_HANDE_VAUE)或者取為–1。pFieMappingAttributes:一個(gè)指向SECURITY_ATTRIBUTES結(jié)構(gòu)的指針,它指明返回的句柄是否可以被子進(jìn)程所繼承。另外,在SECURITY_ATTRIBUTES結(jié)構(gòu)中,也包括一個(gè)安全性描述的子指針。fProtect:允許指定內(nèi)存塊的訪問權(quán)限,權(quán)限值有PAGE_READONY、PAGE_READWRITE和PAGE_WRITECOPY,PAGE_WRITECOPY。dwMaximumSizeHigh和dwMaximumSizeow

指定了文件的最大字節(jié)數(shù),由于這兩個(gè)參數(shù)共64位,因此所支持的最大文件長度為16EB,幾乎可以滿足任何大數(shù)據(jù)量文件處理的要求。pName:內(nèi)存映射對象指定名字,通過調(diào)用CreateFieMapping()函數(shù)和Open-FieMapping()函數(shù),其他進(jìn)程可用這個(gè)名字來訪問相同的文件映射。在調(diào)用CreateFieMapping()時(shí),可以用GetastError()來檢查其返回的錯(cuò)誤信息。如果返回值為ERROR_AREADY_EXISTS,則表示內(nèi)存映射對象指定名字已經(jīng)存在。有關(guān)其他返回值的意義見MSDN的詳細(xì)說明。一旦某個(gè)內(nèi)存映射對象由CreateFieMapping()創(chuàng)建成功,就可以調(diào)用MapView-OfFie()函數(shù),把文件視圖映射到進(jìn)程地址空間上,這個(gè)函數(shù)需要使用一個(gè)由CreateFieMapping()函數(shù)或OpenFieMapping()函數(shù)返回的句柄,并允許指定訪問模式和映射的字節(jié)數(shù),以及文件映射對象中的偏移量。MapViewOfFie()函數(shù)的原型為:

PVOIDMapViewOfFie(

HANDEhFieMappingObject,

DWORDdwDesiredAccess,

DWORDdwFieOffsetHigh,

DWORDdwFieOffsetow,

DWORDdwNumberOfBytesToMap

);其中參數(shù)含義如下所述。hFieMappingObject:為CreateFieMapping()返回的文件映射對象句柄。dwDesiredAccess:再次指定了對文件數(shù)據(jù)的訪問方式,而且同樣要與CreateFie-Mapping()函數(shù)所設(shè)置的保護(hù)屬性相匹配。dwFieOffsetHigh和dwFieOffsetow:分別為文件偏移的高32位和低32位。dwNumberOfBytesToMap:為映射視圖的大小。另外,還可以使用MapViewOfFieEx()函數(shù)來實(shí)現(xiàn)同樣的功能,此函數(shù)還允許調(diào)用進(jìn)程為映射視圖指定特殊的內(nèi)存地址,但是如果指定的內(nèi)存地址空間大小不夠,則函數(shù)執(zhí)行失敗。MapViewOfFieEx()函數(shù)的原型為:

PVOIDMapViewOfFieEx(

HANDEhFieMappingObject,

DWORDdwDesiredAccess,

DWORDdwFieOffsetHigh,

DWORDdwFieOffsetow,

DWORDdwNumberOfBytesToMap,

PVOIDpBaseAddress

);

其中參數(shù)pBaseAddress指定映射視圖的實(shí)際內(nèi)存地址。其他參數(shù)與MapViewOfFie()函數(shù)中的相同。在完成對映射到進(jìn)程地址空間區(qū)域的文件處理后,需要通過函數(shù)UnmapViewOfFie()完成對文件數(shù)據(jù)映射的釋放,該函數(shù)原型為:

BOOUnmapViewOfFie(PCVOIDpBaseAddress);

其中參數(shù)pBaseAddress為MapViewOfFie()函數(shù)的返回值。在使用了MapViewOfFie()函數(shù)之后,必須要有對應(yīng)的UnmapViewOfFie()函數(shù)調(diào)用,否則在進(jìn)程終止之前,保留的區(qū)域?qū)o法釋放。除此之外,在進(jìn)程終止之前還必須要用CoseHande()將文件句柄釋放,否則將會出現(xiàn)資源泄漏的問題。

用內(nèi)存映射在多個(gè)應(yīng)用程序之間共享數(shù)據(jù)內(nèi)存映射文件是在多個(gè)進(jìn)程之間共享一個(gè)數(shù)據(jù)塊的重要方法。通過調(diào)用API函數(shù)CreateFieMapping()可以像對待一個(gè)文件那樣對待一個(gè)內(nèi)存區(qū)。本節(jié)編寫了兩個(gè)程序:一個(gè)傳輸數(shù)據(jù)(Send.exe),另一個(gè)接收數(shù)據(jù)(Recv.exe)。本例中共享的數(shù)據(jù)很簡單,只是一個(gè)字符串。在Send程序的對話框上輸入一個(gè)字符串,然后單擊【發(fā)送】命令,把所輸入的字符串放入內(nèi)存文件里。Recv程序中設(shè)定一個(gè)定時(shí)器用于監(jiān)測內(nèi)存映射文件中的字符串,當(dāng)然可以用一個(gè)工作線程來完成此操作,只是用Timer時(shí)間控制器較方便。當(dāng)內(nèi)存映射文件改變時(shí),在時(shí)間控制器的作用下,Recv程序?qū)υ捒蛏弦矊⒏聰?shù)據(jù)。圖1.2顯示了兩個(gè)程序之間的通信。在進(jìn)程之間共享數(shù)據(jù)的Send和Recv應(yīng)用程序這兩程序是在VisuaC++的MFC下編寫,創(chuàng)建它們的步驟如下所述。(1)創(chuàng)建Send應(yīng)用程序的步驟①用MFC的AppWizard(exe)創(chuàng)建新項(xiàng)目Send取Projectname為Send,單擊“確定”按鈕后進(jìn)入創(chuàng)建應(yīng)用程序類型,選擇Diaogbased類型并按下Finish按鈕。②增加CSendDg中的成員變量通過CassWizard創(chuàng)建變量m_strSend用于傳遞字符串,增加成員變量m_hMapObject和m_pszMapView以記錄內(nèi)存映射文件的句柄和映射。

//SendDg.h:頭文件//#if!defined(AFX_SENDDG_H__INCUDED_)#defineAFX_SENDDG_H__INCUDED_////////////////////////////////////////////////////////////////////////CSendDgdiaogcassCSendDg:pubicCDiaog{//Constructionpubic:

CSendDg(CWnd*pParent=NU);//standardconstructor

~CSendDg();//DiaogData

//{{AFX_DATA(CSendDg)

enum{IDD=IDD_SEND_DIAOG};

CStringm_strSend;

//}}AFX_DATA

//CassWizardgeneratedvirtuafunctionoverrides

//{{AFX_VIRTUA(CSendDg)

protected:

virtuavoidDoDataExchange(CDataExchange*pDX);

//DDX/DDVsupport

//}}AFX_VIRTUA//Impementationprotected:

HICONm_hIcon;

HANDEm_hMapObject;

PSTRm_pszMapView;

//Generatedmessagemapfunctions

//{{AFX_MSG(CSendDg)

virtuaBOOOnInitDiaog();

afx_msgvoidOnSend();

//}}AFX_MSG

DECARE_MESSAGE_MAP()};#endif//!defined(AFX_SENDDG_H__INCUDED_)③編輯CSendDg::OnInitDiaog()編輯OnInitDiaog()以創(chuàng)建該內(nèi)存映射文件,并將文件的視圖映射到進(jìn)程的地址空間。如果內(nèi)存映射文件創(chuàng)建成功,通過MapViewOfFie()即得到在內(nèi)存中起始地址的指針,該指針保存在成員變量m_pszMapView中。以下代碼中所創(chuàng)建的映射文件大小為4096(0x1000)個(gè)字節(jié)。

BOOCSendDg::OnInitDiaog(){

CDiaog::OnInitDiaog();

//Settheiconforthisdiaog.

Theframeworkdoesthisautomaticay

//whentheappication'smainwindowisnotadiaog

SetIcon(m_hIcon,TRUE);

//Setbigicon

SetIcon(m_hIcon,FASE);

//Setsmaicon

m_hMapObject=CreateFieMapping((HANDE)0xFFFFFFFF,NU,

PAGE_READWRITE,0,

0x1000,_TEXT("shared_memory"));

if(!m_hMapObject){

AfxMessageBox("Unabetocreatesharedmemoryfie.");

returnFASE;

}

//Mapviewoffieintoouraddressspace

m_pszMapView=(PSTR)MapViewOfFie

(m_hMapObject,FIE_MAP_WRITE,0,0,0);

if(!m_pszMapView){

AfxMessageBox(_T("Unabetomapsharedmemoryfie."));

returnFASE;

}

returnTRUE;

//returnTRUE

unessyousetthefocustoacontro}

④用MFCCassWizard添加CSendDg::OnSend()函數(shù)當(dāng)在輸入框里輸入了一個(gè)字符串,例如“ShareMemoryFie”,單擊【發(fā)送】按鈕后,將m_strSend字符串賦值給m_pszMapView,以便能夠讓Recv.exe程序調(diào)用。將如下的代碼添加到OnSend()函數(shù)中。

voidCSendDg::OnSend(){

UpdateData();

strcpy(m_pszMapView,m_strSend);}

(2)創(chuàng)建Recv應(yīng)用程序的步驟①用MFC的AppWizard(exe)創(chuàng)建新項(xiàng)目Recv取Projectname為Recv,單擊“確定”按鈕后進(jìn)入創(chuàng)建應(yīng)用程序類型,選擇Diaogbased類型并單擊Finish按鈕。②修改CRecvDg類文件頭通過CassWizard創(chuàng)建變量m_strText用于顯示接收到的字符串,與Send.exe一樣,增加成員變量m_hMapObject和m_pszMapView以記錄內(nèi)存映射文件的句柄和映射,并且增加析構(gòu)函數(shù)~CRecvDg(),用于釋放內(nèi)存映射文件的句柄和文件映射。增加時(shí)間控制器函數(shù)OnTimer(),它相當(dāng)于一個(gè)服務(wù)程序。

//RecvDg.h:頭文件//#if!defined(AFX_RECVDG_H__INCUDED_)#defineAFX_RECVDG_H__INCUDED_////////////////////////////////////////////////////////////////////////CRecvDgdiaogcassCRecvDg:pubicCDiaog{//Constructionpubic:

CRecvDg(CWnd*pParent=NU);//standardconstructor

~CRecvDg();//DiaogData

//{{AFX_DATA(CRecvDg)

enum{IDD=IDD_RECV_DIAOG};

CStringm_strText;

//}}AFX_DATA

//CassWizardgeneratedvirtuafunctionoverrides

//{{AFX_VIRTUA(CRecvDg)

protected:

virtuavoidDoDataExchange(CDataExchange*pDX);

//DDX/DDVsupport

//}}AFX_VIRTUA//Impementationprotected:

HICONm_hIcon;

HANDEm_hMapObject;

PSTRm_pszMapView;

//Generatedmessagemapfunctions

//{{AFX_MSG(CRecvDg)

virtuaBOOOnInitDiaog();

afx_msgvoidOnTimer(UINTnIDEvent);

//}}AFX_MSG

DECARE_MESSAGE_MAP()};#endif//!defined(AFX_RECVDG_H__INCUDED_)

③增加析構(gòu)函數(shù)CRecvDg::~CRecvDg()在析構(gòu)函數(shù)中釋放內(nèi)存映射文件的句柄和映射。在程序關(guān)閉時(shí),把所使用內(nèi)存交還給系統(tǒng),以保證系統(tǒng)有充分的內(nèi)存資源。

CRecvDg::~CRecvDg(){

//Unmapviewoffie

if(!UnmapViewOfFie(m_pszMapView)){

AfxMessageBox("Unabetounmapviewoffie.");

}

//Cosethesharedmemoryfie

CoseHande(m_hMapObject);}

④編輯CRecvDg::OnInitDiaog()在此函數(shù)中,首先使用名字“shared_memory”打開內(nèi)存映射文件。如果Open-FieMapping()成功,即得到內(nèi)存映射文件的第一個(gè)字節(jié)的指針。然后,用SetTimer()函數(shù)設(shè)定時(shí)間控制器。

BOOCRecvDg::OnInitDiaog(){

CDiaog::OnInitDiaog();

//Settheiconforthisdiaog.

Theframeworkdoesthisautomaticay

//

whentheappication'smainwindowisnotadiaog

SetIcon(m_hIcon,TRUE);

//Setbigicon

SetIcon(m_hIcon,FASE);

//Setsmaicon

//Openmemorymappedfie.

m_hMapObject=OpenFieMapping(FIE_MAP_READ,FASE,_TEXT("shared_mem

ory"));

if(!m_hMapObject){

AfxMessageBox("Can'topensharedmemoryfie.");

returnFASE;

}

//Getpointertoshareddata.

m_pszMapView=(PSTR)MapViewOfFie

(m_hMapObject,FIE_MAP_READ,0,0,0);

if(!m_pszMapView){

AfxMessageBox("Can'tmapviewofsharedmemoryfie.");

returnFASE;

}

SetTimer(0x10,900,NU);

returnTRUE;

//returnTRUE

unessyousetthefocustoacontro.}

⑤編輯CRecvDg::OnTimer()先用CassWizard添加這個(gè)時(shí)間控制器函數(shù),然后將如下的代碼添加到OnTimer()函數(shù)中。這個(gè)函數(shù)只有在調(diào)用SetTimer()函數(shù)后才起作用。

voidCRecvDg::OnTimer(UINTnIDEvent){

m_strText=m_pszMapView;

UpdateData(FASE);

CDiaog::OnTimer(nIDEvent);}

用內(nèi)存映射文件讀取大型文件通常情況下,用文件讀寫函數(shù)對文件進(jìn)行處理,如Win32API的CreateFie()、WriteFie()、ReadFie()等。一般來說,以上這些函數(shù)可以滿足大多數(shù)場合的要求,但是對于某些特殊應(yīng)用領(lǐng)域需要幾十GB、幾百GB的海量存儲,用通常的處理方法進(jìn)行文件處理顯然是行不通的。目前,對于這種大文件的操作一般是以內(nèi)存映射的方式來加以處理的。下面以實(shí)例說明如何使用內(nèi)存映射來處理文件。(1)創(chuàng)建MemFie應(yīng)用程序的步驟在VisuaC++的MFC下編寫這個(gè)程序的步驟如下所述。①用MFC的AppWizard(exe)創(chuàng)建新項(xiàng)目MemFie取Projectname為MemFie,單擊“確定”按鈕后進(jìn)入創(chuàng)建應(yīng)用程序類型,選擇Diaogbased類型并單擊Finish按鈕。②CMemFieDg類文件頭用CassWizard添加成員變量m_strText以顯示一個(gè)文件內(nèi)容,增加一個(gè)成員函數(shù)oadFie()以實(shí)現(xiàn)用內(nèi)存映射處理文件的操作。

//MemFieDg.h:頭文件//#if!defined(AFX_MEMFIEDG_H__INCUDED_)#defineAFX_MEMFIEDG_H__INCUDED_////////////////////////////////////////////////////////////////////////CMemFieDgdiaogcassCMemFieDg:pubicCDiaog{//Constructionpubic:

CMemFieDg(CWnd*pParent=NU);

//standardconstructor

~CMemFieDg();//DiaogData

//{{AFX_DATA(CMemFieDg)

enum{IDD=IDD_MEMFIE_DIAOG};

CStringm_strText;

//}}AFX_DATA

//CassWizardgeneratedvirtuafunctionoverrides

//{{AFX_VIRTUA(CMemFieDg)

protected:

virtuavoidDoDataExchange(CDataExchange*pDX);//DDX/DDVsupport

//}}AFX_VIRTUA//Impementationprotected:

HICONm_hIcon;

BOOoadFie(CStringstrFieName);

//Generatedmessagemapfunctions

//{{AFX_MSG(CMemFieDg)

virtuaBOOOnInitDiaog();

//}}AFX_MSG

DECARE_MESSAGE_MAP()};#endif//!defined(AFX_MEMFIEDG_H__INCUDED_)③編輯CMemFieDg::OnInitDiaog()增加oadFie()函數(shù)的調(diào)用,再增加UpdateData()函數(shù)以便使來自映射文件中的內(nèi)容得到顯示。文件test_memfie.txt為事先準(zhǔn)備好的,其內(nèi)容任意給定。

BOOCMemFieDg::OnInitDiaog(){

CDiaog::OnInitDiaog();

//Settheiconforthisdiaog.

Theframeworkdoesthisautomaticay

//whentheappication'smainwindowisnotadiaog

SetIcon(m_hIcon,TRUE);

//Setbigicon

SetIcon(m_hIcon,FASE);

//Setsmaicon

oadFie("c:\\test_memfie.txt");

UpdateData(FASE);

returnTRUE;

//returnTRUE

unessyousetthefocustoacontro}

④增加CMemFieDg::oadFie()函數(shù)首先用CreateFie()來創(chuàng)建一個(gè)讀文件的句柄,如果創(chuàng)建失敗,函數(shù)返回FASE。接著用CreateFieMapping()創(chuàng)建一個(gè)文件映射對象。同樣,如果創(chuàng)建失敗,函數(shù)返回FASE。然后用MapViewOfFie()獲得文件第一個(gè)字節(jié)的指針。通過該指針可以得到文件中的全部內(nèi)容。最后使用CoseHande()關(guān)閉文件對象和文件映射對象,并且使用UnmapViewOfFie()來釋放文件數(shù)據(jù)映射。

BOOCMemFieDg::oadFie(CStringstrFieName){

HANDEhFie,hMapping;

void*basepointer;

//Createfieobject.

if((hFie=CreateFie(strFieName,GENERIC_READ,

FIE_SHARE_READ,0,OPEN_EXISTING,

FIE_FAG_SEQUENTIA_SCAN,0))==INVAID_HANDE_VAUE)

{

AfxMessageBox("Coudnotopenfie.");

returnFASE;

}

//Createsanamedfiemappingobject.

if(!(hMapping=CreateFieMapping(hFie,0,PAGE_READONY|SEC_COMMIT,0,0,0)))

{

AfxMessageBox("Mappingfaied.");

CoseHande(hFie);

returnFASE;

}

//Mapviewoffieintobaseointer.

if(!(basepointer=MapViewOfFie(hMapping,FIE_MAP_READ,0,0,0)))

{

AfxMessageBox("Viewfaied.");

CoseHande(hMapping);

CoseHande(hFie);

returnFASE;

}

//Cosefiemappingobject.

CoseHande(hMapping);

//Cosefieobject.

CoseHande(hFie);

//Getthecontextofthefie.

m_strText=(PSTR)basepointer;

//Unmapviewoffie.

UnmapViewOfFie(basepointer);

returnTRUE;}

(2)用內(nèi)存映射文件處理文件的實(shí)驗(yàn)首先用notepad創(chuàng)建一個(gè)文件,其文件名為test_memfie.txt,文件的內(nèi)容設(shè)為“北京是中國的首都”,并把這個(gè)文件存放在C:\目錄下。運(yùn)行應(yīng)用程序MemFie.exe后,則在其窗口中顯示“北京是中國的首都”這樣的字符串,如圖1.3所示。用內(nèi)存映射文件處理文件的MemFie應(yīng)用程序從上面的實(shí)例可以看出,用內(nèi)存映射文件處理文件時(shí)不需要用文件數(shù)據(jù)的讀取函數(shù),如ReadFie()等,這給文件的處理,特別是大文件的處理,帶來極大的方便。1.5深入認(rèn)識指針的真正含義要學(xué)好C語言,就必須要學(xué)好指針,而對于初學(xué)者來說,很多人對指針的含義很模糊,以至越學(xué)越糊涂。這里將從內(nèi)存的角度分析指針的真正含義。指針的真正本質(zhì)什么是指針?所謂指針其實(shí)就是一個(gè)變量或一個(gè)函數(shù)在計(jì)算機(jī)內(nèi)存中的地址。也許把指針直接稱為“地址”更為確切,更容易被人理解。在C語言中,任何變量在使用前都必須先要進(jìn)行定義,否則就會編譯出錯(cuò),這是因?yàn)樾枰o這個(gè)變量在內(nèi)存中申請一個(gè)地址空間。有了一個(gè)內(nèi)存地址空間后,這個(gè)變量值就可以存放在這個(gè)地址上。比如說一間房子,那么這個(gè)房子的地址就是“指針”,其名稱則是“指針”值,而房子里面的東西就是“變量值”。對于一個(gè)變量,如何才能獲得它的地址呢?C語言中提供了一種能夠獲取變量地址的方法,也就是獲得某個(gè)變量指針的方法。例如:intxint*yx=10;*y=20;在上面的代碼中,變量x和*y在定義時(shí)就在內(nèi)存中申請到一個(gè)地址。在C編譯時(shí),編譯器將給這兩個(gè)變量各分配一個(gè)地址代碼;在Windows系統(tǒng)中,它為一個(gè)虛地址。用操作&x可以得到變量x的虛地址代碼值,而y就是變量*y的虛地址代碼值。也就是,&x和y分別是變量x和*y變量的指針。當(dāng)這兩個(gè)變量分別申請到一個(gè)內(nèi)存地址空間后,就可以分別給它們賦值了,即上面代碼x=10和*y=20的語句。在申請變量地址時(shí),所申請到的變量地址代碼值與該變量在程序代碼中的相對位置有關(guān)。有興趣的話可以做實(shí)驗(yàn)來認(rèn)識這一點(diǎn)。如果能夠獲得一個(gè)應(yīng)用程序中某個(gè)變量的地址代碼值,就可以在另外一個(gè)應(yīng)用程序中通過內(nèi)存訪問操作來改變這個(gè)變量值。用指針進(jìn)行應(yīng)用程序之間的通信以下通過一個(gè)實(shí)例來說明用指針如何進(jìn)行應(yīng)用程序間的通信。這里將給出兩個(gè)應(yīng)用程序,其中一個(gè)為服務(wù)程序,另一個(gè)為控制程序。先在服務(wù)程序中獲得某個(gè)變量的指針,然后在控制程序中通過ReadProcessMemory()和WriteProcessMemory()這兩個(gè)函數(shù)來對這個(gè)指針地址的內(nèi)容進(jìn)行讀和寫。當(dāng)然這個(gè)指針值也可以通過一個(gè)DEBUG工具,如Oydbg等軟件來獲得。兩個(gè)應(yīng)用程序之間用指針進(jìn)行通信的方法如圖1.4所示。對話框TestServer由CTestServerDg類建立,它為一個(gè)服務(wù)程序,而對話框TestCtr由CTestCtrDg類建立,它是一個(gè)控制程序。用指針進(jìn)行通信的TestServer和TestCtr應(yīng)用程序在實(shí)際操作時(shí),先在CTestServerDg對象中獲得m_dwVaue的指針(如本實(shí)例0x12fee8),并且給m_dwVaue賦值。在CTestServerDg類中,使用Timer時(shí)間控制器更新窗口界面。(1)創(chuàng)建TestServer應(yīng)用程序同樣用MFCAppWizard來創(chuàng)建應(yīng)用程序TestServer,其窗口類型為對話框。在CTestServerDg類的頭文件中,字符串m_strPointer用來存放變量m_dwVaue的指針,即內(nèi)存地址值。當(dāng)m_dwVaue的值發(fā)生改變時(shí),用OnTimer函數(shù)來更新它在TestServer對話框上的顯示。在應(yīng)用程序TestServer編譯后,m_strVaue的地址是固定的。CTestServerDg類的頭文件代碼如下://TestServerDg.h:頭文件//#if!defined(AFX_TESTSERVERDG_H__INCUDED_)#defineAFX_TESTSERVERDG_H__INCUDED_////////////////////////////////////////////////////////////////////////CTestServerDgdiaogcassCTestServerDg:pubicCDiaog{//Constructionpubic:CTestServerDg(CWnd*pParent=NU);//standardconstructor//DiaogData//{{AFX_DATA(CTestServerDg)enum{IDD=IDD_TESTSERVER_DIAOG};CStringm_strPointer;CStringm_strVaue;//}}AFX_DATA//CassWizardgeneratedvirtuafunctionoverrides//{{AFX_VIRTUA(CTestServerDg)protected:virtuavoidDoDataExchange(CDataExchange*pDX);//DDX/DDVsupport//}}AFX_VIRTUA//Impementationprotected:HICONm_hIcon;DWORDm_dwVaue;//Generatedmessagemapfunctions//{{AFX_MSG(CTestServerDg)virtuaBOOOnInitDiaog();afx_msgvoidOnTimer(UINTnIDEvent);//}}AFX_MSGDECARE_MESSAGE_MAP()};#endif//!defined(AFX_TESTSERVERDG_H__INCUDED_)在下面的CTestServerDg::OnInitDiaog函數(shù)中,調(diào)用SetTimer()以使OnTimer()起作用,其中給變量m_dwVaue賦一個(gè)初值;在CTestServerDg::OnTimer()中,先計(jì)算變量m_dwVaue的指針,并把m_dwVaue類型變成字符串m_strVaue。最后,調(diào)用UpdateData()使字符串能在窗口界面上顯示。這些成員函數(shù)的源代碼如下:BOOCTestServerDg::OnInitDiaog(){CDiaog::OnInitDiaog();//Settheiconforthisdiaog.Theframeworkdoesthisautomaticay//whentheappication'smainwindowisnotadiaogSetIcon(m_hIcon,TRUE);//SetbigiconSetIcon(m_hIcon,FASE);//SetsmaiconSetTimer(1,900,NU);m_dwVaue=20060901;returnTRUE;//returnTRUEunessyousetthefocustoacontro.}voidCTestServerDg::OnTimer(UINTnIDEvent){m_strPointer.Format("0x%x",&m_dwVaue);m_strVaue.Format("%d",m_dwVaue);UpdateData(FASE);CDiaog::OnTimer(nIDEvent);}(2)創(chuàng)建TestCtr應(yīng)用程序同樣,程序TestCtr用MFCAppWizard來創(chuàng)建,其窗口類型也為對話框。要對TestServer程序地址0x12fee8上的內(nèi)容進(jìn)行讀或取,TestCtr程序需要獲得TestServer程序的進(jìn)程句柄。這就要求TestServer程序在TestCtr程序之前先運(yùn)行起來,否則尋找TestServer程序進(jìn)程操作將會失敗。在CTestCtrDg類頭文件中增加了讀和寫字符串,相應(yīng)地定義了讀和寫函數(shù)。CTestCtrDg類頭文件的代碼如下://TestCtrDg.h:頭文件//#if!defined(AFX_TESTCTRDG_H__INCUDED_)#defineAFX_TESTCTRDG_H__INCUDED_////////////////////////////////////////////////////////////////////////CTestCtrDgdiaogcassCTestCtrDg:pubicCDiaog{//Constructionpubic:CTestCtrDg(CWnd*pParent=NU);//standardconstructor//DiaogData//{{AFX_DATA(CTestCtrDg)enum{IDD=IDD_TESTCTR_DIAOG};CStringm_strRead;CStringm_strWrite;//}}AFX_DATA//CassWizardgeneratedvirtuafunctionoverrides//{{AFX_VIRTUA(CTestCtrDg)protected:virtuavoidDoDataExchange(CDataExchange*pDX);//DDX/DDVsupport//}}AFX_VIRTUA//Impementationprotected:HICONm_hIcon;//Generatedmessagemapfunctions//{{AFX_MSG(CTestCtrDg)virtuaBOOOnInitDiaog();afx_msgvoidOnRead();afx_msgvoidOnWrite();//}}AFX_MSGDECARE_MESSAGE_MAP()};#endif//!defined(AFX_TESTCTRDG_H__INCUDED_)在下面函數(shù)中,用FindWindow()函數(shù)查找到TestServer窗口后,得到其進(jìn)程的句柄,然后用ReadProcessMemory()或WriteProcessMemory()函數(shù)來讀取或改變TestServer程序地址0x12fee8上的內(nèi)容。voidCTestCtrDg::OnRead(){DWORDpid;HWNDhWnd=::FindWindow(NU,TEXT("TestServer"));if(!hWnd){AfxMessageBox("沒有找到TestServer進(jìn)程.");return;}::GetWindowThreadProcessId(hWnd,&pid);HANDEhProcess=::OpenProcess(PROCESS_A_ACCESS,FASE,pid);PVOIDpBaseAddress=(PVOID)0x12fee8;DWORDdwVaue;if(!::ReadProcessMemory(hProcess,pBaseAddress,(void*)&dwVaue,sizeof(DWORD),0))return;m_strRead.Format("%d",dwVaue);UpdateData(FASE);}voidCTestCtrDg::OnWrite(){DWORDpid;HWND

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論