一、 Windows 内存管理
- 地址空间:32位操作系统空间0~ 2^32-1(4G),地址空间越大程序越易于编写。
- 地址空间的划分:
2.1 用户地址空间 0 - 2G(0x7FFFFFFF )存放用户的程序和数据。用户空间的代码是不能访问内核空间的数据和代码。
2.1.1 空指针区(NULL区,0-64K)系统将地址小于64K指针,都认为是空指针。
2.1.2 用户区 64K~ 0x7FFEFFFF,存放用户程序的代码和数据
2.1.3 64K禁入区(0x7FFEFFFF - 0x7FFFFFFF )
2.2 内核地址空间 2G - 4G
存放内核的代码和数据,例如系统驱动。
内核空间代码是可以访问用户空间。
- 区域:连续的一块内存。区域的大小一般为64K或者64K倍数。每个区域都有自己的状态
1)空闲:没有被使用
2)私有:被预定的区域
3)映像:存放代码
4)映射:存放数据
- 物理内存(半导体,内存条):系统可以使用的实际内存。CPU可以直接访问的内存。
- 虚拟内存(磁盘交换文件):将硬盘文件虚拟成内存使用。(pagefile.sys 文件)CPU如果要访问虚拟内存数据,必须将虚拟内存数据放到物理内存。经常使用的数据存放在物理内存中,不经常使用的数据存放在虚拟内存中。
- 内存页:系统管理内存的最小单位。内存页大小为4K,每个内存页有自己的权限。
- 页目表:
- 指针地址
31 22 21 12 11 0
|------------------|------------------|-------------------|
10位 10位 12位
2^10=1024 1024 4K
页目 页表
包含1K个页表 包含1K个页 包含4K个字节
页目中包含1K项,每项对应一个页表,页表包含1K个项,每项对应一个页,页中包含4k字节 ---- 1K*1K*4K=4 G
- 内存获取数据,访问过程
a) CPU根据地址在物理内存中查找相应的位置。如果找到物理内存,取回数据。如果未找到,执行b)。
b) 根据地址去虚拟内存中查找相应的位置。如果未找到,那么该地址没有内存空间,返回错误(野指针)。如果找到,执行c)。
c) 将该地址所在内存页,置换到物理内存中,同时将原物理内存数据,存入到虚拟内存中。
d) 将物理内存中的数据返回给使用者。
- 内存分配
a) 虚拟内存分配-适合大内存分配,一般是1M之上的内存。
b) 堆内存分配-适合小内存分配,一般是1M以下的内存。malloc/new
c) 栈内存分配-适合小内存分配,一般是1M以下的内存。
d)
- 虚拟内存:
a) 虚拟内存分配:速度快,大内存效率高。将内存和地址分配分别执行,可以先分配内存地址,在需要的时候再将地址绑定(提交)到内存。常用字大型电子表格等处理。
b) 虚拟内存的分配:
LPVOID VirtualAlloc(
LPVOID lpAddress,// NULL或提交地址
SIZE_T dwSize, //分配的大小
DWORD flAllocationType, //分配方式
DWORD flProtect //内存访问方式
); //分配成功返回虚拟内存地址,失败返回NULL。
flAllocationType分配方式:
MEM_COMMIT - 提交内存分配之后返回地址和内存空间.(内存和地址同时分配)
MEM_RESERVE- 保留地址,分配之后只返回地址,内存空间不生成(不绑定到内存)。要使用内存必须再次提交执行,即再次执行VirtualAlloc(第一次返回的提交地址, ** , MEM_COMMIT , ** )。
不足一页分配或是跨页地址分配,则操作系统会将从跨页的低位地址开始分配的。即按照页边界对齐的原则。内存绑定(提交)以页(4096字节)为单位。
获取内存状态:
VOID GlobalMemoryStatus(
LPMEMORYSTATUS lpBuffer // 内存状态结构
);
参数说明:内存状态结构:
typedef struct _MEMORYSTATUS { // mst
DWORD dwLength; // 结构体字节数
DWORD dwMemoryLoad; // 内存使用率,百分之
DWORD dwTotalPhys; // 物理内存总字节数
DWORD dwAvailPhys; // 空闲物理内存字节数
DWORD dwTotalPageFile; // 分页文件总字节数
DWORD dwAvailPageFile; // 空闲分页文件字节数
DWORD dwTotalVirtual; // 虚拟内存总字节数
DWORD dwAvailVirtual; // 空闲虚拟内存字节数
} MEMORYSTATUS, *LPMEMORYSTATUS;
c) 虚拟内存的释放:
BOOL VirtualFree(
LPVOID lpAddress,//释放地址
SIZE_T dwSize, //释放的大小,字节数
DWORD dwFreeType //释放方式
); //成功返回true,失败返回false。
dwSize : 0, 表示全部释放
释放方式:
MEM_DECOMMIT - 只释放内存,不释放地址。
MEM_RELEASE - 地址和内存都释放。
- 堆内存:
a) 堆内存分配:适合分配小内存,一般是小于1M的内存。一般每个程序都有自己的堆,默认大小为1M,会根据使用情况需要进行动态调整。
b) 堆的使用
- 堆的信息:
- 获取调用进程的首个堆:
HANDLE GetProcessHeap (void);
//成功返回调用进程首个堆的句柄,失败返回NULL。
- 获取调用进程的所有堆
DWORD GetProcessHeaps(
DWORD NumberOfHeaps, //堆句柄数组的容量
PHANDLE ProcessHeaps // 保存返回堆句柄数组
);//成功返回进程堆数目,失败返回0。
- 创建堆:
HANDLE HeapCreate(
DWORD flOptions,//创建选项
SIZE_T dwInitialSize, //初始字节数,以后可调整
SIZE_T dwMaximumSize //最大字节数,0表示无穷大
); 成功返回堆句柄,失败返回NULL。
参数说明:
flOptions:
HEAP_GENERATE_EXCEPTIONS 堆内存分配失败则引发异常
HEAP_NO_SERIALIZE 支持不连续存取
- 从堆中分配内存
LPVOID HeapAlloc(
HANDLE hHeap, //堆句柄
DWORD dwFlags, //分配方式
SIZE_T dwBytes //分配内存大小
); 成功返回地址,失败返回NULL。
参数说明:
HEAP_GENERATE_EXCEPTIONS 堆内存分配失败则引发异常
HEAP_NO_SERIALIZE 支持不连续存取,
(若在创建堆的时候已经指定此参数,则此参数将忽略.)。
HEAP_ZERO_MEMORY 初始化清零
- 释放堆内存:
BOOL HeapFree(
HANDLE hHeap, // 堆句柄
DWORD dwFlags, // 释放方式,只能取HEAP_NO_SERIALIZE
LPVOID lpMem // 释放堆内存地址
); //成功返回true,失败返回false。
- 销毁堆:
BOOL HeapDestroy(
HANDLE hHeap //堆句柄
); //成功返回true ,失败返回false
当堆被销毁后,其中该堆分配的内存均将释放。
c) Win32 的malloc / new 实现实际是调用了上述堆函数。即:
VirtualAlloc/HeapAlloc/malloc/new在Windows平台上,函数调用关系:
new/malloc -> HeapAlloc ->VirtualAlloc
- 栈内存
a) 栈内存-每个线程都具有自己的栈,默认大小1M,一般是系统维护栈。
b) Windows提供了 _alloca 函数, 用于在栈上分配内存。
c) 操作系统负责自动维护栈内存的分配与释放。
- 内存映射文件:
a) 本质:将文件映射成内存来使用。当使用内存时,就是在使用文件。
常用于实现进程间通信,比直接通过文件I/O效率更高。
b) 内存映射的使用:
- 创建/ 打开文件CreateFile。设置可读可写属性.
- 创建映射:
HANDLE CreateFileMapping(
HANDLE hFile, //文件句柄
LPSECURITY_ATTRIBUTES lpAttributes, //安全属性设为NULL
DWORD flProtect,//访问方式可读写,PAGE_READWRITE
DWORD dwMaximumSizeHigh,//内存映射文件大小的高32位
DWORD dwMaximumSizeLow, //内存映射文件大小的低32位
LPCTSTR lpName //映射名,NULL表示匿名映射,其他进程不可访问
); // 创建成功返回映射文件句柄,失败返回NULL。
- 加载映射文件:
LPVOID MapViewOfFile(
HANDLE hFileMappingObject,//内存映射文件句柄
DWORD dwDesiredAccess,//访问模式,FILE_MAP_ALL_ACCESS
DWORD dwFileOffsetHigh, //偏移量的高32位
DWORD dwFileOffsetLow, //偏移量的低32位
SIZE_T dwNumberOfBytesToMap //映射的字节数量
); 成功返回地址,失败返回NULL。
参数说明:
dwFileOffsetHigh和dwFileOffsetLow合成的偏移量,必须是区域粒度的整数倍(64K的整数倍)
使用映射,即对该返回地址的操作即可。
- 使用映射(内存):以内存的方式使用该映射
- 卸载内存映射:
BOOL UnmapViewOfFile(
LPCVOID lpBaseAddress //映射的地址
); .//成功返回true,失败返回false。
- 关闭(销毁)映射:
BOOL CloseHandle(
HANDLE hObject // 句柄
);
- 关闭文件:
CloseHandle,同上
- 对于已建好的映射,打开映射:
HANDLE OpenFileMapping(
DWORD dwDesiredAccess, // 访问方式,FILE_MAP_ALL_ACCESS
BOOL bInheritHandle, // 子进程是否继承此函数所返回的句柄
LPCTSTR lpName //映射名
);// 成功返回映射句柄,失败返回NULL。
c) 基于内存映射文件的进程间的通信
- 写进程:创建文件- 创建映射- 加载映射- 写入映射- 卸载映射 –
销毁映射- 关闭文件
- 读进程:打开映射- 加载映射- 读取映射- 卸载映射- 销毁映射
- 关于句柄:
句柄就是内存对象地址在句柄表中索引。通过句柄不能直接访问内存,但是可以通过APIs函数操作其标识的对象。
二、 Windows 进程
1. 基本概念
1) 进程是一个容器,包含程序执行所需要的代码,数据。资源、等信息。
Windows 是一个多任务操作系统,可以同时执行多个进程。
2. 进程特点
1)每个进程都有自己的ID号
2)每个进程都有自己的地址空间,进程之间无法访问对方的地址空间。
3)每个进程都有自己的安全属性
4)每个进程当中至少包含一个线程
3. 进程环境信息
获取和释放环境信息
获取
LPVOID GetEnvironmentStrings(VOID);//返回当前进程环境变量地址
释放
BOOL FreeEnvironmentStrings(
LPTSTR lpszEnvironmentBlock // 进程环境块指针
);//成功返回true,失败返回false。
4. 获取和设置环境变量
设置环境变量:
BOOL SetEnvironmentVariable(
LPCTSTR lpName, //变量名
LPCTSTR lpValue // 变量值
);//成功返回true,失败返回false。
获取环境变量:
DWORD GetEnvironmentVariable(
LPCTSTR lpName, // 变量名
LPTSTR Buffer, //变量值缓冲区
DWORD Size // 变量值缓冲区大小(字符为单位含尾空字符)
);//返回存在变量缓冲区的字符数,不含尾空字符
若没有找到缓冲区变量名,则返回0。
5. 进程信息:
1) 获取进程ID:
DWORD GetCurrentProcessId(VOID);//返回调用进程的ID。
另外,int _getpid( void );也可以返回进程的ID,
2) 获取进程句柄:
HANDLE GetCurrentProcess(VOID);//返回调用进程的伪句柄(-1),可以使用该句柄访问该进程的所用操作。但其子进程不继承该句柄
6. 进程的使用:
1)创建进程:
WinExec - Win6 遗留,现在基本不用。
ShellExecute - Shell 操作 ,速度慢。
Createprocess 目前使用最多
BOOL CreateProcess(
LPCTSTR lpApplicationName,//应用程序名称路径
LPTSTR lpCommandLine, //命令行参数
LPSECURITY_ATTRIBUTES lpProcessAttributes, //进程安全属性
LPSECURITY_ATTRIBUTES lpThreadAttributes,
//线程安全属性,NULL为缺省属性,同上。
BOOL bInheritHandles, //子进程是否可以继承父进程的句柄
DWORD dwCreationFlags, //创建方式,0表示立即启动
LPVOID lpEnvironment, //子进程环境,NULL表示继承父进程环境
如:”book=c++ pen=color 0”其中的