Win32编程day13 学习笔记
Windows的内存管理
一 地址空间
1 地址空间
一个程序最大的寻址范围。对于Win32操作系统最大的寻址范围是2的32次方,0-0xFFFFFFFF。这个寻址范围由CPU决定。CPU的寻址范围越大,编程难度降低。
2 地址空间的划分
通常情况下:
2.1 用户空间
地址范围 0 - 0x7FFFFFFF(2G),运行应用程序代码、数据等等。
2.2.1 空指针区(NULL区)
地址范围 0 - 0x0000FFFF
2.2.2 用户区
地址范围 0x00010000 - 0x7FFEFFFF
2.2.3 64K禁入区
地址范围 0x7FFEFFFF - 0x7FFFFFFF
2.2 内核空间
地址范围 0x80000000 - 0xFFFFFFFF,被系统使用,运行驱动、内核的数据和代码。
二 地址映射
1 区域
区域指一段连续的地址空间,区域的粒度和CPU的粒度、操作系统相关。目前通常都是以64K粒度存在,地址的对齐方式是以64K为边界。
区域的状态:
1)空闲 - 空闲的,可以被使用
2)私有 - 已经被占有,但是还未使用
3)映像 - 程序的代码使用
4)映射 - 程序的数据使用
2 物理内存
实际可以使用的物理存储器。
3 虚拟内存
使用硬盘空间作为内存扩展,也可以当作物理内存使用。
4 内存页
操作系统使用内存页的方式管理物理内存和虚拟内存。通常情况下,内存页的大小为4K或者8K。
每个内存页具有自己的状态,例如 只读/可写/可执行
5 页目表
用于管理内存页的表。
页目 - 页表 - 内存页
- 内存页
- 页表
- 页表
指针 31 -----22 21-------12 11-----------0
页目 页表 偏移量
6 地址空间的访问
6.1 地址空间已经存在映射好的物理内存,直接使用,返回。
6.2 系统去虚拟内存中,查找对应的内存页。如果未找到,系统错误返回。
6.3 系统将虚拟内存的内存页切换到物理内存当中。
6.4 返回实际物理内存地址,使用数据。

// WinSys.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "windows.h" void ShowSys( ) { SYSTEM_INFO info = { 0 }; GetSystemInfo( &info ); printf( "内存页的大小: %d\n", info.dwPageSize ); printf( "可用最小地址: %p\n", info.lpMinimumApplicationAddress ); printf( "可用最大地址: %p\n", info.lpMaximumApplicationAddress ); printf( "区域的分配粒度: %d\n", info.dwAllocationGranularity ); } int main(int argc, char* argv[]) { ShowSys( ); //错误的地址 //printf( "%d", *((int *)0x0000FFFF) ); return 0; }
7 内存的使用
7.1 虚拟内存
适合对于大内存分配使用。一般情况下如果分配的内存大于1M,应该使用虚拟内存分配方式。
7.2 堆内存
适合对于小内存分配使用。一般情况下对于小于1M的内存分配使用。例如
malloc/new。
7.3 堆栈内存
系统维护的内存区。
二 虚拟内存
1 虚拟内存
常用于大内存分配,分配的速度快,可以根据需要指定分配方式。
2 虚拟内存的使用
2.1 分配内存
LPVOID VirtualAlloc( LPVOID lpAddress,//NULL或者用于提交的内存地址 DWORD dwSize,//分配的大小,一般是页倍数 DWORD flAllocationType,//分配的方式 DWORD flProtect );//内存访问方式
分配的最大空间小于用户区间(通常是2G)。
2.2 提交内存
VirtualAlloc使用MEM_COMMIT方式。例如:
pszBuf = (CHAR *)VirtualAlloc( pszBuf, //需要提交内存地址 1024 * 1024 * 1024, MEM_COMMIT, PAGE_READWRITE );
2.3 使用内存
2.4 释放内存
BOOL VirtualFree( LPVOID lpAddress,//释放的内存 DWORD dwSize, //释放的大小 DWORD dwFreeType ); //释放的方式
3 内存信息
VOID GlobalMemoryStatus( LPMEMORYSTATUS lpBuffer //获取内存信息 );

// WinVirtual.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "conio.h" #include "windows.h" void Status( ) { //获取内存信息 MEMORYSTATUS status = { 0 }; status.dwLength = sizeof( status ); GlobalMemoryStatus( &status ); printf( "TotalPhys: %u\n", status.dwTotalPhys ); printf( "AvailPhys: %u\n", status.dwAvailPhys ); printf( "TotalPageFile: %u\n", status.dwTotalPageFile ); printf( "AvailPageFile: %u\n", status.dwAvailPageFile ); printf( "TotalVirtual: %u\n", status.dwTotalVirtual ); printf( "AvailVirtual: %u\n", status.dwAvailVirtual ); printf( "MemoryLoad: %d\n", status.dwMemoryLoad ); } void Virtual( ) { Status( ); //地址分配 CHAR * pszBuf = (CHAR *) VirtualAlloc( NULL, 1024 * 1024 * 1024, MEM_RESERVE, PAGE_READWRITE ); printf( "MEM_RESERVE: %p\n", pszBuf ); Status( ); getch( ); //内存提交 pszBuf = (CHAR *) VirtualAlloc( pszBuf, 1024 * 1024 * 1024, MEM_COMMIT, PAGE_READWRITE ); printf( "MEM_COMMIT: %p\n", pszBuf ); Status( ); strcpy( pszBuf, "hello Virtual" ); printf( "%s\n", pszBuf ); Status( ); getch( ); //释放内存 VirtualFree( pszBuf, 1024 * 1024 * 1024, MEM_RELEASE ); } int main(int argc, char* argv[]) { Virtual( ); return 0; }
三 堆内存
1 堆内存的特点
一般分配小数据内存,一般小于1M数据使用堆内存分配。
一般程序执行后,会有一个默认堆,这个堆的大小一般为1M。一个程序可以多个堆。通过堆内存管理器来管理堆中的内存。
内存分配速度比VirtualAlloc慢。
2 堆内存的使用
2.1 创建堆
HANDLE HeapCreate( DWORD flOptions,//创建标示 DWORD dwInitialSize, //初始化大小 DWORD dwMaximumSize ); //最大大小
2.2 分配内存
LPVOID HeapAlloc( HANDLE hHeap, //堆的句柄 DWORD dwFlags, //分配标示 DWORD dwBytes ); //分配大小
2.3 使用内存
2.4 释放内存
BOOL HeapFree( HANDLE hHeap, //堆的句柄 DWORD dwFlags, //释放标示 LPVOID lpMem ); //释放的地址
2.5 释放堆
BOOL HeapDestroy( HANDLE hHeap ); //堆的句柄
3 malloc/HeapAlloc/VirtualAlloc
malloc内部调用HeapAlloc。
HeapAlloc内部调用的VirtualAlloc。
malloc分配内存:
例如100字节
| 内存头 | 100字节 | 4字节尾部标示 |
所用使用malloc分配的内存,会使用这个内存头构成链表.
4 堆的信息
GetProcessHeap 当前进程默认堆的句柄
GetProcessHeaps 当前进程所有堆的句柄

// WinHeap.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "stdlib.h" #include "windows.h" void HeapInfo( ) { //默认堆的句柄 HANDLE hHeap = GetProcessHeap(); printf( "Default Heap: %p\n", hHeap ); //所有的堆的句柄 HANDLE hHeaps[256] = { 0 }; DWORD nCount = GetProcessHeaps( 256, hHeaps ); printf( "All Heap: %d\n", nCount ); for( DWORD nIndex=0; nIndex<nCount; nIndex++ ) { printf( "\t%d: %p\n", nIndex+1, hHeaps[nIndex] ); } } void Heap( ) { HeapInfo( ); //创建堆 HANDLE hHeap = HeapCreate( HEAP_GENERATE_EXCEPTIONS, 1024 * 1024, 0 ); printf( "HeapCreate: %p\n", hHeap ); HeapInfo( ); //内存分配 CHAR * pszBuf = ( CHAR * ) HeapAlloc( hHeap, HEAP_ZERO_MEMORY, 100 ); //分配后初始化为0 printf( "HeapAlloc: %p\n", pszBuf ); strcpy( pszBuf, "hello Heap" ); printf( "%s\n", pszBuf ); //内存释放 HeapFree( hHeap, 0, pszBuf ); //释放堆 HeapDestroy( hHeap ); HeapInfo( ); } int main(int argc, char* argv[]) { CHAR * pszBuf = (CHAR *)malloc( 1024 ); Heap( ); return 0; }
四 堆栈内存
堆栈都是小数据的使用,由系统维护,栈的大小一般在1M左右.
例如,Windows下可以使用_alloca函数从栈上分配内存.很少会用
五 内存映射文件
1 内存映射文件
可以将文件映射成内存,我们可以像使用内存一样使用文件.
2 内存映射文件的使用
2.1 创建或打开一个文件
CreateFile
2.2 创建内存映射文件
HANDLE CreateFileMapping( HANDLE hFile, //文件句柄 LPSECURITY_ATTRIBUTES lpFileMappingAttributes,//安全属性 DWORD flProtect, //保护模式 DWORD dwMaximumSizeHigh,//大小的高32位 DWORD dwMaximumSizeLow, //大小的低32位 LPCTSTR lpName ); //文件映射内核对象的名称
2.3 映射成内存地址
LPVOID MapViewOfFile( HANDLE hFileMappingObject, //文件映射句柄 DWORD dwDesiredAccess, //访问模式 DWORD dwFileOffsetHigh, //地址偏移高32位 DWORD dwFileOffsetLow,//地址偏移低32位 DWORD dwNumberOfBytesToMap ); //要映射的字节数
2.4 使用内存
2.5 卸载映射
BOOL UnmapViewOfFile( LPCVOID lpBaseAddress //卸载的地址 );
2.6 关闭内存映射文件
CloseHandle
2.7 文件关闭
CloseHandle

// WinMap.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "windows.h" void Map( ) { //创建文件 HANDLE hFile = CreateFile( "C:\\map.dat", GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); //创建文件映射 HANDLE hMap = CreateFileMapping( hFile, NULL, PAGE_READWRITE, 0, 1024 * 1024, NULL ); //映射地址 CHAR * pszText = (CHAR *)MapViewOfFile( hMap, FILE_MAP_ALL_ACCESS, 0, 0, 1024 * 1024 ); //使用内存 strcpy( pszText, "Hello File Mapping" ); printf( "%s\n", pszText ); //卸载地址 UnmapViewOfFile( pszText ); //关闭文件映射 CloseHandle( hMap ); //关闭文件 CloseHandle( hFile ); } int main(int argc, char* argv[]) { Map( ); return 0; }