《匯編語言:基于X86處理器》第11章 MS-Windows編程(3)

本章展示的是如何用32 位Microsoft Windows API進行控制臺窗口編程。應用編程接口(API:ApplicationProgramming Interface)是類型、常數和函數的集合體,它提供了一種用計算機代碼操作對象的方式。本章將討論文本I/O、顏色選擇、時間與日期、數據文件I/O,以及內存管理的API 函數。同時,還包括了本書 64 位鏈接庫Irvine64代碼的一些例子。

?

11.3 動態內存分配

動態內存分配(dynamic memoryallocation),又被稱為堆分配(heap allocation),是編程語言使用的一種技術,用于在創建對象、數組和其他結構時預留內存。比如在 Java 語言中,下面的語句就會為 String對象保留內存:

String str = new String("abcde");

同樣的,在 C++中,對變量使用大小屬性就可以為一個整數數組分配空間:

int size;
cin >> size;			//用戶輸入大小
int array[] = new int[size];

C、C++和Java都有內置運行時堆管理器來處理程序請求的存儲分配和釋放。程序啟動時,堆管理器常常從操作系統中分配一大塊內存,并為存儲塊指針創建空閑列表(free list)。當接收到一個分配請求時,堆管理器就把適當大小的內存塊標識為已預留,并返回指向該塊的指針。之后,當接收到對同一個塊的刪除請求時,堆就釋放該內存塊,并將其返回到空閑列表。每次接收到新的分配請求,堆管理器就會掃描空閑列表,尋找第一個可用的、且容量足夠大的內存塊來響應請求。

匯編語言程序有兩種方法進行動態分配。方法一,通過系統調用從操作系統獲得內存塊。方法二,實現自己的堆管理器來服務更小的對象提出的請求。本節展示的是如何實現第一種方法。示例程序為 32 位保護模式的應用程序。

利用表 11-11 中的幾個 Win32 API 函數就可以從 Windows 中請求多個不同大小的內存塊。表中所有的函數都會覆蓋通用寄存器,因此程序可能想要創建封裝過程來實現重要寄存器的人棧和出棧操作。若需進一步了解存儲管理,請在 Microsoft 在線文檔中查閱MemoryManagement Reference

表11-11 堆相關函數

函數

描述

GetProcessHeap

用EAX返回程序現存堆區域的32位整數句柄。如果函數成功,則EAX中的返回值為堆句柄。如果函數失敗,則EAX中的返回值為NULL

HeapAlloc

從堆中分配內存塊。如果成功,EAX中的返回值就為內存塊的地址。如果失敗,則EAX中的返回值為 NULL

HeapCreate

創建新堆,并使其對調用程序可用。如果函數成功,則EAX中的返回值為新創建堆的句柄。如果失敗,則 EAX 的返回值為NULL

HeapDestroy

銷毀指定堆對象,并使其句柄無效。如果函數成功,則EAX中的返回值為非零

HeapFree

釋放之前從堆中分配的內存塊,該堆由其地址和堆句柄進行標識。如果內存塊釋放成功,則返回值為非零

HeapReAlloc

對堆中內存塊進行再分配和調整大小。如果函數成功,則返回值為指向再分配內存塊的指針。如果函數失敗,且沒有指定HEAP_GENERATE_EXCEPTIONS,則返回值為NULL

HeapSize

返回之前通過調用HeapAlloc或 HeapReAlloc 分配的內存塊的大小。如果函數成功,則 EAX包含被分配內存塊的字節數。如果函數失敗,則返回值為SIZET1(SIZET等于指針能指向的最大字節數)

GetProcessHeap如果使用的是當前程序的默認堆,那么GetProcessHeap 就足夠了這個函數沒有參數,EAX中的返回值就是堆句柄:

GetProcessHeap PROTO

示例調用:

.data
hHeap HANDLE ?
.code
INVOKE GetProcessHeap
.IF eax == NULL				;不能獲取句柄jmp quit
.ELSEmov hHeap, eax			;句柄 OK
.ENDIF

HeapCreate HeapCreate能為當前程序創建一個新的私有堆:

HeapCreate PROTO,flOptions:DWORD,			;堆分配選項dwInitialSize:DWORD,	    ;按字節初始化堆大小dwMaximumSize:DWORD		;最大堆字節數

flOptions設置為NULL。dwInitialSize設置為初始堆字節數,其值的上限為下一頁的邊界。如果 HeapAlloc 的調用超過了初始堆大小,那么堆最大可以擴展到dwMaximumSize參數中指定的大小(上限為下一頁的邊界)。調用后,EAX 中的返回值為空就表示堆未創建成功。HeapCreate 的調用示例如下:

HEAP_START = 2000000					;2MB
HEAP_MAX = 400000000					;400M
.data
hHeap HANDLE ?							;堆句柄
.code
INVOKE HeapCreate, 0, HEAP_START, HEAP_MAX
.IF eax == NULL							;堆未創建call WriteWindowsMsg				    ;顯示錯誤消息jmp quit
.ELSEmov hHeap, eax						;句柄OK
.ENDIF

HeapDestroyHeapDeatroy銷毀一個已存在的私有堆(由HeapCreate創建)。需向其傳遞堆句柄:

HeapDestroy PROTO,hHeap:DWORD						;堆句柄

如果堆銷毀失敗,則 EAX 等于NULL。下面為示例調用,其中使用了11.1.4 節描述的WriteWindowsMsg 過程:

.data
hHeap HANDLE ?						;堆句柄
.code
INVOKE HeapDestroy, hHeap
.IF eax == NULLcall WriteWindowsMsg				;顯示錯誤消息
.ENDIF

HeapAlloc HeapAlloc從已存在堆中分配一個內存塊:

HeapAlloc PROTO,hHeap:HANDLE,					;現有堆內存塊的句柄dwFlags:DWORD,				;堆分配控制標志dwBytes:DWORD					;分配的字節數

需傳遞下述參數:·

●hHeap,32位堆句柄,該堆由GetProcessHeap或HeapCreate初始化。

●dwFlags,一個雙字,包含了一個或多個標志值。可以選擇將其設置為HEAP_ZERO MEMORY,即設置內存塊為全零。

●dwBytes,一個雙字,表示堆分配的字節數。

如果 HeapAlloc 成功,則 EAX 包含指向新存儲區的指針;如果失敗,則 EAX 中的返回值為NULL。下面的代碼用 hHeap 標識一個堆,從該堆中分配了一個1000 字節的數組,并將數組初始化為全零:

.data
hHeap HANDLE ?					;椎句柄
pArray DWORD ?					;數組指針
.code
INVOKE HeapAlloc, hHeap, HEAP_ZERO_MEMORY, 1000
.IF eax == NULLmWrite "HeapAlloc failed"jmp quit
.ELSEmov pArray,eax
.ENDIF

HeapFree 函數 HeapFree 釋放之前從堆中分配的一個內存塊,該堆由其地址和堆句柄標識:

HeapFree PROTO,hHeap:HANDLE,dwFlags:DWORD,lpMem:DWORD

第一個參數是包含該內存塊的堆的句柄。第二個參數通常為零,第三個參數是指向將被釋放內存塊的指針。如果內存塊釋放成功,則返回值非零。如果該塊不能被釋放,則函數返回零。示例調用如下:

INVOKE HeapFree, hHeap;0, pArray

Error Handling 若在調用HeapCreate、HeapDestroy或GetProcessHeap時遇到錯誤,可以通過調用API 函數GetLastError 來獲得詳細信息。還可以調用Irvine32鏈接庫的函數WriteWindowsMsg。HeapCreate調用示例如下:

.IF eax == NULL					;失敗?call WriteWindowsMsg			;顯示錯誤信息
.ELSEmov hHeap, eax				;成功
.ENDIF

反之,函數 HeapAlloc 在失敗時不會設置系統錯誤碼,因此也就無法調用 GetLastError或WriteWindowsMsg。

11.3.1 HeapTest程序

下面的程序示例(Heaptest1.asm)使用動態內存分配創建并填充了一個1000 字節的數組:

;11.3.1_HeapTest1.asm      11.3.1 HeapTest程序
;下面的程序示例(Heaptest1.asm)使用動態內存分配創建并填充了一個1000 字節的數組:INCLUDE Irvine32.inc;使用動態內存分配,本程序分配并填充一個字節數組
.data
ARRAY_SIZE = 1000
FILL_VAL EQU 0FFhhHeap	HANDLE ?								;程序堆句柄
pArray	DWORD ?									;內存塊指針
newHeap	DWORD ?									;新堆句柄
str1 BYTE "Heap size is: ", 0.code
main PROCINVOKE GetProcessHeap						;獲取程序堆句柄.IF eax == NULL								;如果失敗,顯示消息call WriteWindowsMsgjmp quit.ELSEmov hHeap, eax								;成功.ENDIFcall allocate_arrayjnc arrayOk									;失敗(CF=1)?call WriteWindowsMsgcall Crlfjmp quitarrayOk:										;成功填充數組call fill_arraycall display_arraycall Crlf;釋放數組INVOKE HeapFree, hHeap, 0, pArrayquit:INVOKE ExitProcess, 0
main ENDP
;---------------------------------------------------------
;動態分配數組空間。
;接收:EAX=程序堆句柄
;返回:如果內存分配成功,則CF=0。
;----------------------------------------------------------
allocate_array PROC USES eaxINVOKE HeapAlloc, hHeap, HEAP_ZERO_MEMORY, ARRAY_SIZE.IF eax == NULLstc										;返回 CF=1.ELSEmov pArray, eax							;保存指針clc										;返回CF=0.ENDIFret
allocate_array ENDP
;---------------------------------------------------------
;用一個字符填充整個數組。
;接收:無
;返回:無
;----------------------------------------------------------
fill_array PROC USES ecx edx esimov ecx, ARRAY_SIZE							;循環計數器mov esi, pArray								;指向數組
L1:	mov BYTE PTR [esi], FILL_VAL				;填充每個字節inc esi										;下一個位置loop L1ret
fill_array ENDP
;---------------------------------------------------------
;顯示數組。
;接收:無
;返回:無
;----------------------------------------------------------
display_array PROC USES eax ebx ecx esimov ecx, ARRAY_SIZE							;循環計數器mov esi, pArray								;指向數組
L1:	mov al, [esi]								;取出一個字節mov ebx, TYPE BYTEcall WriteHexB								;顯示該字節inc esi										;下一個位置loop L1ret
display_array ENDP
END main

運行調試:

下面的示例(Heaptest2.asm)采用動態內存分配重復分配大塊內存,直到超過堆大小。

;11.3.1_HeapTest2.asm      11.3.1 HeapTest程序
;下面的示例(Heaptest2.asm)采用動態內存分配重復分配大塊內存,直到超過堆大小。INCLUDE Irvine32.inc.data
HEAP_START = 2000000							;2MB
HEAP_MAX = 400000000							;400MB
BLOCK_SIZE = 500000								;0.5MBhHeap HANDLE ?									;堆句柄
pData DWORD ?									;塊指針
str1 BYTE 0dh, 0ah, "Memory allocation failed", 0dh, 0ah, 0.code
main PROCINVOKE HeapCreate, 0, HEAP_START, HEAP_MAX.IF eax == NULL								;失敗?call WriteWindowsMsgcall Crlfjmp quit.ELSEmov hHeap, eax								;成功.ENDIFmov ecx, 2000								;循環計數器	
L1:	call allocate_block							;分配一個塊.IF Carry?									;失敗?mov edx, OFFSET str1						;顯示消息call WriteStringjmp quit.ELSEmov al, '.'									;否:打印一個點來顯示進度call WriteChar.ENDIF;call free_block							;允許/禁止本行loop L1
quit:INVOKE HeapDestroy, hHeap					;銷毀堆.IF eax == NULL								;失敗?call WriteWindowsMsg						;是:錯誤消息call Crlf.ENDIFINVOKE ExitProcess, 0
main ENDP
allocate_block PROC USES ecx;分配一個塊,并填充為全零.INVOKE HeapAlloc, hHeap, HEAP_ZERO_MEMORY, BLOCK_SIZE.IF eax ==  NULLstc										;返回CF = 1.ELSEmov pData, eax							;保存指針clc										;返回CF = 0.ENDIFret
allocate_block ENDP
free_block PROC USES ecxINVOKE HeapFree, hHeap, 0, pDataret
free_block ENDPEND main

運行調試:

11.3.2 本節回顧

1.在C、C++和Java上下文中,堆分配的另一個術語是什么?

答:動態內存分配。

2.請說明 GetProcessHeap函數。

答:用EAX為程序現有堆返回一個32位整數句柄。

3.請說明 HeapAlloc 函數。

答:從堆中分配一個內存塊。

4.給出 HeapCreate 函數的一個示例調用。

HEAP_START = 2000000		;2MB
HEAP_MAX = 400000000		;400M
.data
hHeap HANDLE ?
.code
INVOKE HeapCreate, 0, HEAP_START, HEAP_MAX

5.調用HeapDestroy時,如何標識將被銷毀的內存塊?

答:(與堆句柄一起)傳遞內存塊指針。

11.4 x86存儲管理

本節將對 Windows 32 位存儲管理進行簡要說明,展示它是如何使用 x86 處理器直接內置功能的。重點關注的是存儲管理的兩個主要方面:

●將邏輯地址轉換為線性地址

●將線性地址轉換為物理地址(分頁)

下面先簡單回顧一下第2章介紹過的一些x86存儲管理術語:

●多任務處理(multitasking)允許多個程序(或任務)同時運行。處理器在所有運行程序中劃分其時間。

●段(segments)是可變大小的內存區,用于讓程序存放代碼或數據。

●分段(segmentation)提供了分隔內存段的方法。它允許多個程序同時運行又不會相互干擾。

●段描述符(segment descriptor)是一個64 位的值,用于標識和描述一個內存段。它包含的信息有段基址、訪問權限、段限長、類型和用法。

現在再增加兩個新術語:

●段選擇符(segmentselector)是保存在段寄存器(CS、DS、SSESFS或GS)中的一個16 位數值。

●邏輯地址(logicaladdress)就是段選擇符加上一個32位的偏移量。

本書一直都忽略了段寄存器,因為用戶程序從來不會直接修改這些寄存器,所以只關注了 32 位數據偏移量。但是,從系統程序員的角度來看,段寄存器是很重要的,因為它們包含了對內存段的直接引用。

11.4.1 線性地址

1.邏輯地址轉換為線性地址

多任務操作系統允許幾個程序(任務)同時在內存中運行。每個程序都有自己唯一的數據區。假設現有3個程序,每個程序都有一個變量的偏移地址為200h,那么,怎樣區分這3個變量而不進行共享? x86 解決這個問題的方法是,用一步或兩步處理過程將每個變量的偏移量轉換為唯一的內存地址。

第一步,將段值加上變量偏移量形成線性地址(linear address)。這個線性地址可能就是該變量的物理地址。但是像MS-Windows和Linux這樣的操作系統采用了分頁(paging)功能,它使得程序能使用比可用物理空間更大的線性空間。這種情況下,就必需采用第二步頁轉換(page translation),將線性地址轉換為物理地址。頁轉換將在 11.4.2 節介紹。

首先了解一下處理器如何用段和選擇符來確定變量的線性地址。每個段選擇符都指向一個段描述符(位于描述符表中),其中包含了該內存段的基地址。如圖11-6所示,邏輯地址中的32位偏移量加上段基址就形成了32位的線性地址,

線性地址線性地址是一個32位整數,其范圍為OFFFFFFFFh,它表示一個內存位置。如果禁止分頁功能,那么線性地址也就是目標數據的物理地址。

2.分頁

分頁是x86處理器的一-個重要功能,它使得計算機能運行在其他情況下無法裝人內存的一組程序。處理器初始只將部分程序加載到內存,而程序的其他部分仍然留在硬盤上。程序使用的內存被分割成若干小區域,稱為頁(page),通常一頁大小為4KB。當每個程序運行時處理器會選擇內存中不活躍的頁面替換出去,而將立即會被請求的頁加載到內存。

操作系統通過維護一個頁目錄(pagedirectory)和一組頁表(page table)來持續跟蹤當前內存中所有程序使用的頁面。當程序試圖訪問線性地址空間內的一個地址時,處理器會自動將線性地址轉換為物理地址。這個過程被稱為頁轉換(page translation)。如果被請求頁當前不在內存中,則處理器中斷程序并產生一個頁故障(page fault)。操作系統將被請求頁從硬盤復制到內存,然后程序繼續執行。從應用程序的角度看,頁故障和頁轉換都是自動發生的。

使用Microsoft Windows工具任務管理器(taskmanager)就可以查看物理內存和虛擬內存的區別。圖 11-7 所示計算機的物理內存為256MB。任務管理器的 Commit Charge 框內為當前可用的虛擬內存總量。虛擬內存的限制為633MB,大大高于計算機的物理內存。

圖11-7 Windows任務管理器示例

3.描述符表

段描述符可以在兩種表內找到:全局描述符表(global description table)和局部描述符表(local description table)。

全局描述符表(GDT) 開機過程中,當操作系統將處理器切換到保護模式時,會創建唯一一張 GDT,其基址保存在GDTR(全局描述符表寄存器)中。表中的表項(稱為段描述符)指向段。操作系統可以選擇將所有程序使用的段保存在GDT中。

局部描述符表(LDT) 在多任務操作系統中,每個任務或程序通常都分配有自己的段描述符表,稱為LDT。LDTR寄存器保存的是程序LDT的地址。每個段描述符都包含了段在線性地址空間內的基地址。一般,段與段之間是相互區分的。如圖11-8所示,圖中有三個不同的邏輯地址,這些地址選擇了LDT中三個不同的表項。這里,假設禁止分頁,因此線性地址空間也是物理地址空間。

4.段描述符詳細信息

除了段基址,段描述符還包含了位映射字段來說明段限長和段類型。只讀類型段的一個例子就是代碼段。如果程序試圖修改只讀段,則會產生處理器故障。

段描述符可以包含保護等級,以便保護操作系統數據不被應用程序訪問。下面是對每個描述符字段的說明:

基址: 32位整數,定義段在4GB線性地址空間中的起始地址。

特權級: 每個段都可以分配一個特權級,特權級范圍從0到 3,其中0級為最高級,一般用于操作系統核心代碼。如果特權級數值高的程序試圖訪問特權級數值低的段,則發生處理器故障。

段類型: 說明段的類型并指定段的訪問類型以及段生長的方向(向上或向下)。數據(包括堆棧)段可以是可讀類型或讀/寫類型,其生長方向可以是向上的也可以是向下的。代碼段可以是只執行類型或執行/只讀類型。

段存在標志: 這一位說明該段當前是否在物理內存中。

粒度標志: 確定對段限長字段的解釋。如果該位清零,則段限長以字節為單位。如果該位置1,則段限長的解釋單位為4096字節。

段限長: 這個20位的整數指定段大小。按照粒度標志,這個字段有兩種解釋:

●該段有多少字節,范圍為1~IMB。

●該段包含多少個4096字節,允許段大小的范圍為4KB~4GB。

11.4.2 頁轉換

若允許分頁,則處理器必須將 32位線性地址轉換為 32 位物理地址2。這個過程會用到3種結構:

●頁目錄:一個數組,最多可包含 1024 個 32 位頁目錄項。

●頁表:一個數組,最多可包含 1024 個32 位頁表項。

●頁:4KB或4MB的地址空間。

為了簡化下面的敘述,假設頁面大小為 4KB:

線性地址分為三個字段:頁目錄表項指針、頁表項指針和頁內偏移量。控制寄存器CR3)保存了頁目錄的起始地址。如圖11-9所示,處理器在進行線性地址到物理地址的轉換時,采用如下步驟:

1)線性地址引用線性地址空間中的一個位置。

2)線性地址中10位的目錄字段是頁目錄項的索引。頁目錄項包含了頁表的基址。

3)線性地址中 10 位的頁表字段是頁表的索引,該頁表由頁目錄項指定。索引到的頁表項包含了物理內存中頁面的基址。

4)線性地址中 12 位的偏移量字段與頁面基址相加,生成的恰好是操作數的物理地址。

操作系統可以選擇讓所有的運行程序和任務使用一個頁目錄,或者選擇讓每個任務使用一個頁目錄,還可以選擇為兩者的組合。

Windows虛擬機管理器

現在對IA-32如何管理內存已經有了總體了解,那么看看Windows 如何處理內存管理可能也會令人感興趣。下面這段文字轉自 Microsoft 在線文檔:

虛擬機管理器(VMM)是Windows內核中的32位保護模式操作系統。它創建運行、監視和終止虛擬機。它管理內存、進程、中斷和異常。它與虛擬設備(virtualdevice)一起工作,使得它們能攔截中斷和故障,以此來控制對硬件和已安裝軟件的訪問。VMM和虛擬設備運行在特權級為0的單一32位平坦模式地址空間中。系統創建兩個全局描述符表項(段描述符),一個是代碼段的,一個是數據段的。段固定在線性地址0。VMM 提供多線程和搶先多任務處理。通過共享運行應用程序的虛擬機之間的CPU時間,它可以同時運行多個應用程序。

在上面的文字中,可以將虛擬機解釋為Intel中的過程或任務。它包含了程序代碼、支撐軟件、內存和寄存器。每個虛擬機都被分配了自己的地址空間、I/0端口空間、中斷向量表和局部描述符表。運行于虛擬8086模式的應用程序特權級為3。Windows中保護模式程序的特權級為0和3。

11.4.3 本節回顧

1.術語解釋:

a.多任務 b.分段

答:(a)多任務允許同時運行多個程序(或任務)。處理器將其時間分割給所有的運行程序。

(b)分段是指隔離內存段與內存段的方法。它使得多個程序能同時運行且不會相互干擾。

2.術語解釋:

a.段選擇符 b.邏輯地址

答:(a)段選擇符是保存在段寄存器(CS、DS、SS、ES、FS或GS)中的16位值。

(b)邏輯地址選擇符與32位偏移量的組合。

3.(真/假):段選擇符指向段描述符表的一個表項。

答:真

4.(真/假):段描述符包含了段的基地址。

答:真

5.(真/假):段選擇符是32位的。

答:假

6.(真/假):段描述符不包含段大小信息。

答:假

11.5 本章

小結表面上看,32 位控制臺模式程序的外觀和行為就像運行在文本模式下的16 位MS-DOS程序。這兩種類型的程序都從標準輸人讀,向標準輸出寫,支持命令行重定向,還可以顯示彩色文本。但是,深人了解會發現,Win32 控制臺與 MS-DOS 程序是有很大不同的。Win32運行于 32 位保護模式,而 MS-DOS 運行于實地址模式。Win32 程序可以調用圖形Windows應用程序使用的函數庫內的函數。而 MS-DOS 程序只局限于BIOS 的一個小子集,以及從出現IBM-PC 后就存在的MS-DOS中斷。

Windows API函數使用的字符集類型:8位的ASCII/ANSI 字符集和16 位的Unicode字符集。

API 函數使用的標準MS-Windows 數據類型必須轉換為MASM 數據類型(參見表11-1)。

控制臺句柄為32位整數,用于控制臺窗口的輸人/輸出。函數GetStdHandle獲取控制臺句柄。進行高級控制臺輸入,調用函數ReadConsole;進行高級控制臺輸出,調用WriteConsole。創建和打開文件時,調用 CreateFile。讀文件時,調用ReadFile;寫文件時,調用 WriteFile。CloseHandle 關閉一個文件。移動文件指針,調用 SetFilePointer。

要操作控制臺屏幕緩沖區,調用 SetConsoleScreenBufferSize。要改變文本顏色,調用SetConsoleTextAttribute。本章的程序WriteColors演示了函數 WriteConsoleOutputAttribute和WriteConsoleOutputCharacter。

要獲取系統時間,調用 GetLocalTime;要設置時間,調用SetLocalTime。這兩個函數都要使用SYSTEMTIME 結構。本章的GetDateTime函數示例用64位整數返回日期和時間,指明從1601 年1 月1 日開始經過了多少個100 納秒。函數 TimerStart 和 TimerStop 可用來創建一個簡單的秒表計時器。

創建圖形MS-Windows應用程序時,用該程序的主窗口類信息填充WNDCLASS結構。創建 WinMain 過程獲取當前過程的句柄、加載圖標和光標、注冊程序的主窗口、創建主窗口、顯示和更新主窗口,并開始接收和發送消息的循環。

WinProc 過程負責處理輸入的 Windows 消息,一般由用戶行為激活,比如點擊鼠標或者按鍵。本章的示例程序處理了WM LBUTTONDOWNWMCREATE和WM CLOSE消息。當檢測到相應事件時,就會顯示彈出消息。

動態內存分配,或堆分配是保留和釋放用戶程序所用內存的工具。匯編語言程序有兩種方法來實現動態內存分配。第一種,進行系統調用,從操作系統獲得內存塊。第二種,實現自己的堆管理器來響應小型對象的請求。下面是動態內存分配最重要的 Win32 API調用:

●GetProcessHeap返回程序已存在內存堆區域的32位整數句柄。

●HeapAlloc從堆中分配一個內存塊。

●HeapCreate 新建一個堆。

●HeapDestroy銷毀一個堆。

●HeapFree 釋放之前從堆分配出去的內存塊。

●HeapReAlloc 從堆中重新分配內存塊,并重新定義塊大小。●HeapSize返回之前分配的內存塊的大小。

本章的內存管理小節主要涉及兩個問題:將邏輯地址轉換為線性地址,以及將線性地址轉換為物理地址。

邏輯地址中的選擇符指向段描述符表的表項,這個表項又指向線性空間內的一個段。段描述符包含了段信息,如段大小和訪問類型。描述符表有兩種:唯一的全局描述符表(GDT),以及一個或多個局部描述符表(LDT)。

分頁是x86處理器的一個重要功能,它使得計算機能運行在其他情況下無法裝入內存的一組程序。處理器初始只將部分程序加載到內存,同時,程序的其他部分仍然留在硬盤上。處理器利用頁目錄、頁表和頁面生成數據的物理地址。頁目錄包含了頁表指針。頁表包含了頁面指針。

閱讀 若想進一步閱讀了解 Windows編程,下面的書籍可能會有所幫助:

Mark Russinovich 和David Solomon,《Windows Internals》,第1、2部分,MicrosoftPress,2012.

Barry Kauler,《 Windows Assembly Language and System Programming 》,CMPBooks,1997.

Charles Petzold,《Programming Windows》,第5版,Microsoft Press, 1998

11.6 關鍵術語

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/91723.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/91723.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/91723.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

在 macOS 上通過 Docker 部署DM8 (ARM 架構)

概述 達夢數據庫 (DM8) 無法直接在 Apple macOS 操作系統上原生安裝,通常需要通過虛擬機(如 Parallels Desktop、VMware Fusion)進行部署。另一種更輕量級且受 macOS 支持的方案是利用 Docker 容器技術來構建開發與測試環境。本文檔將詳細介…

網絡協議之路由是怎么回事?

寫在前面 要想去外面的世界看看, 就離不了路由器,而路由器工作的原理就是路由,那么具體是怎么路由的呢?本文就一起來看下這部分內容。 1:路由的配置 配置一條路由無非就是在配置以下三個信息: 1:包要去哪里&#x…

2106. 摘水果,梳理思路

文章目錄題目概要java 解法詳解題目概要 在一個無限的 x 坐標軸上,有許多水果分布在其中某些位置。給你一個二維整數數組 fruits ,其中 fruits[i] [positioni, amounti] 表示共有 amounti 個水果放置在 positioni 上。fruits 已經按 positioni 升序排列…

深入理解消息隊列(MQ)核心原理與設計精髓

引言:從一個“不堪重負”的訂單系統說起想象一個簡化的電商下單流程:用戶點擊“下單”后,系統需要:在訂單數據庫中創建一條記錄。調用庫存服務,扣減商品庫存。調用營銷服務,給用戶發放積分和優惠券。調用通…

前端手撕題總結篇(算法篇——來自Leetcode牛客)

鏈表指定區域反轉 找到區間(頭和為 for循環當**時)->反轉鏈表(返回反轉過后的頭和尾)->連接 function reverseBetween( head , m , n ) {//preEnd&cur&nextStart cur.next斷開if(mn)return head;const vHeadNode…

從Excel到工時管理系統:企業如何選擇更高效的工時記錄工具?

還在為手工統計員工工時而頭疼嗎?月末堆積如山的Excel表格、反復核對的數據、層出不窮的差錯,這些問題正在拖慢企業的發展步伐。8Manage工時管理系統發現,傳統手工記錄不僅耗費大量人力,更讓寶貴的工時數據難以轉化為有效的管理決…

Java設計模式之《命令模式》

目錄 1、介紹 1.1、命令模式定義 1.2、對比 1.3、典型應用場景 2、命令模式的結構 2.1、組成部分: 2.2、整體流程 3、實現 3.1、沒有命令模式 3.2、命令模式寫法 4、命令模式的優缺點 前言 java設計模式分類: 1、介紹 1.1、命令模式定義 命…

【動態規劃算法】路徑問題

什么是動態規劃算法動態規劃(Dynamic Programming,簡稱 DP)是一種通過分解復雜問題為重疊子問題,并存儲子問題的解以避免重復計算,從而高效求解具有特定性質(重疊子問題、最優子結構)問題的算法…

Java基本技術講解

一、基礎語法三要素 暫時無法在飛書文檔外展示此內容 🔑 黃金法則?:每個變量都要聲明類型!二、程序邏輯控制(游戲行為核心) 條件判斷:if-else - “岔路口選擇” // 撿到金幣邏輯 if (isTouching(Coin.clas…

【網絡基礎2】路由器的 “兩扇門”:二層接口和三層接口到底有啥不一樣?

目錄 前言:路由器不是只有 “插網線的口” 一、先搞懂一個基礎:路由器是 “網絡交通樞紐” 二、二層接口:“小區內部的單元門”,只認 “住戶身份證” 1. 啥是二層接口? 2. 用 “小區內部串門” 理解二層接口 步驟 1:手機打包數據,寫上 “收件人身份證” 步驟 2:二…

MLIR TableGen

簡介 TableGen 是一種領域特定語言(DSL),TableGen 的設計目標是允許編寫靈活的描述,并將記錄的通用特性提取出來,從而減少重復代碼并提高代碼的可維護性。 TableGen的工作流程: 前端解析: Ta…

2、docker容器命令 | 信息查看

1、命令總覽命令作用docker ps查看運行中的容器(-a查看所有容器)docker logs [CONTAINER]查看容器日志(-f實時追蹤日志)docker inspect [CONTAINER]查看容器詳細信息(JSON格式)docker stats [CONTAINER]實時…

【MySQL】MySQL中鎖有哪些?

一、按照粒度分類: 粒度越小,并發度越高,鎖開銷越大。 1.全局鎖: 作用: 鎖定整個MySQL實例(所有數據庫)。適用場景: 全庫邏輯部分。(確保備份期間數據的一致性。)實現方式: 通過 FLUSH TABLES W…

語義分割--deeplabV3+

根據論文網絡結構圖講一下:網絡分為兩部分:encoder和decoder部分。 Encoder:DCNN就是主干網絡,例如resnet,Xception,MobileNet這些(主干網絡也要使用空洞卷積),對dcnn的結…

Azure DevOps 中的代理

必知詞匯 深入研究 Azure DevOps 中的代理之前需要掌握的基本概念: 代理:Azure DevOps 中的代理是一個軟件組件,負責執行流水線中的任務和作業。這可能包括數據中心內的物理服務器、本地或云端托管的虛擬機,甚至是容器化環境。這些代理可以在各種操作系統和環境中運行,例如…

AUTOSAR進階圖解==>AUTOSAR_SRS_ADCDriver

AUTOSAR ADC驅動詳解 基于AUTOSAR標準的ADC驅動模塊需求規范分析目錄 ADC驅動模塊概述 關鍵概念定義 ADC驅動架構 ADC驅動在AUTOSAR分層架構中的位置ADC驅動的主要職責 ADC驅動配置結構 通用配置(AdcGeneral)硬件單元配置(AdcHwUnit)通道配置(AdcChannel)通道組配置(AdcChanne…

寶馬集團與SAP聯合打造生產物流數字化新標桿

在德國雷根斯堡的寶馬工廠,每57秒就有一輛新車下線。這座工廠不僅是汽車制造的基地,更是寶馬集團向SAP S/4HANA云平臺轉型的先鋒項目。通過“RISE with SAP”計劃,寶馬將該工廠的運營系統全面遷移至SAP S/4HANA Cloud Private Edition&#x…

Go 語言實戰:構建一個高性能的 MySQL + Redis 應用

引言:為什么是 Go MySQL Redis?在現代后端技術棧中,Go MySQL Redis 的組合堪稱“黃金搭檔”,被廣泛應用于各種高并發業務場景。Go 語言:以其卓越的并發性能、簡潔的語法和高效的執行效率,成為構建高性能…

Excel超級處理器,多個word表格模板中內容提取到Excel表格中

在職場中,很多人習慣在word里插入表格,設計模板,填寫內容,一旦有多個word文件需要整理在excel表格中,最常見的工作方式就是每個word文件打開,復制,粘貼到excel表格里,這樣的工作方式…

前端工程化:ES6特性

本文為個人學習筆記整理,僅供交流參考,非專業教學資料,內容請自行甄別 文章目錄一、let與var1.1、越獄問題1.2、變量的重復聲明1.3、變量提升問題二、解構2.1、數組解構2.2、對象解構2.3、方法解構三、鏈判斷四、參數默認值五、箭頭函數六、模…