概述
投票结果出来了,VirtualAlloc被两人选中,哈哈哈哈,硬着头皮也得更啊,我其实不太了解这个api,投这个的朋友估计跟我想法一样,连平时阅读代码都不见windows api 之VirtualAlloc,正如它的英文名字一样,VirtualAlloc 是 Windows 系统中的内存分配函数,用于在进程的虚拟地址空间中申请一段连续的虚拟内存区域。
我们知道计算机为了更加高效的使用物理内存,设计出了虚拟内存给进程使用,进程需要运行的话只需要将虚拟内存映射到物理内存即可,当然这也属于非常繁琐的计算机科学了,我们一步一步揭开这个api的神秘面纱吧。
目录
一、虚拟地址与物理地址
二、VirtualAlloc |VirtualFree
2.1 VirtualAlloc
2.2 VirtualFree
三、VirtualCopy
四、VirtualQuery
五、总结
一、虚拟地址与物理地址
进程的虚拟地址空间中的虚拟地址是不能直接访问物理内存的。虚拟地址需要被转换成物理地址才能被访问。
在 Windows 系统中,虚拟地址到物理地址的转换是由虚拟内存管理器负责完成的。虚拟内存管理器会根据进程的虚拟地址空间布局和进程可以使用的物理内存来实现虚拟地址到物理地址的转换。
虚拟内存管理器使用一个叫做“页表”的数据结构来维护虚拟地址到物理地址的映射关系。页表是由若干个“页表项”组成的,每个页表项对应一个虚拟内存页(通常是 4KB 大小)。当进程访问一个虚拟地址时,虚拟内存管理器会查询对应的页表项,从而获取对应的物理地址。
当然,对于一些较大的虚拟内存区域,可能会有多个页表项与之对应。这种情况下,虚拟内存管理器会使用一种叫做“分段”的技术来维护这些虚拟内存区域。
总的来说,进程的虚拟地址到物理地址的转换是由虚拟内存管理器负责完成的,并且使用页表这种数据结构来维护虚拟地址到物理地址的映射关系。在 Windows 系统中,进程可以使用一些函数来访问虚拟内存管理器,例如 VirtualAlloc 和 VirtualFree。这些函数可以用来申请、释放和修改虚拟内存区域的属性,以及查询虚拟内存区域的信息。此外,进程还可以使用一些函数来直接访问虚拟内存,例如 VirtualCopy、VirtualProtect 和 VirtualQuery。这些函数可以用来拷贝、修改和查询虚拟内存区域的内容。
有了虚拟内存管理器的帮助,进程可以使用虚拟地址空间来组织自己的内存,并且可以将虚拟地址转换成物理地址来访问真实的内存。这大大提高了进程的灵活性和可扩展性。
二、VirtualAlloc |VirtualFree
2.1 VirtualAlloc
VirtualAlloc 是 Windows 系统中的内存分配函数,用于在进程的虚拟地址空间中申请一段连续的虚拟内存区域。
在 Windows 中,每个进程都有自己的虚拟地址空间,其中包含了进程可以使用的所有虚拟内存。虚拟地址空间可以被划分成若干个虚拟内存区域,每个区域都有一个起始地址和一个长度。VirtualAlloc 函数可以用来在虚拟地址空间中申请一段新的连续的虚拟内存区域。
VirtualAlloc 函数的一个重要用途是在进程的虚拟地址空间中申请一段用于存储代码或数据的内存区域。这段内存区域可以被动态加载或卸载,并且可以在运行时修改。这在某些情况下非常有用,例如在软件保护中使用的加密代码。
VirtualAlloc 函数的语法如下:
LPVOID VirtualAlloc(
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flAllocationType,
DWORD flProtect
);
其中:
- lpAddress:指定内存块的起始地址。如果该参数为 NULL,则系统会自动选择一个合适的内存地址。
- dwSize:指定要分配的内存块的大小(以字节为单位)。
- flAllocationType:指定内存分配的类型。可以是下列值之一:
- MEM_COMMIT:提交内存。
- MEM_RESERVE:保留内存。
- MEM_RESET:清除内存。
- MEM_RESET_UNDO:撤销清除内存。
- MEM_TOP_DOWN:从高地址向低地址分配内存。
- flProtect:指定内存块的保护属性。可以是下列值之一:
- PAGE_EXECUTE:允许执行代码。
- PAGE_EXECUTE_READ:允许执行代码并读取数据。
- PAGE_EXECUTE_READWRITE:允许执行代码、读取和写入数据。
- PAGE_EXECUTE_WRITECOPY:允许执行代码、写入数据并复制。
- PAGE_NOACCESS:禁止访问。
- PAGE_READONLY:允许读取
VirtualAlloc 是 Windows 系统的内存管理函数,可以用来分配虚拟内存或修改已分配的虚拟内存的属性。下面是一个使用 VirtualAlloc 分配内存的示例代码:
#include <windows.h>
int main() {
// 分配一个大小为 1024 字节的虚拟内存块
LPVOID p = VirtualAlloc(NULL, 1024, MEM_COMMIT, PAGE_READWRITE);
if (p == NULL) {
// 分配内存失败
return -1;
}
// 在 p 指向的内存块中写入数据
int* q = (int*)p;
*q = 123;
// 释放虚拟内存
VirtualFree(p, 0, MEM_RELEASE);
return 0;
}
2.2 VirtualFree
VirtualFree 是 Windows 系统的内存管理函数,用于释放虚拟内存块。
语法:
BOOL VirtualFree(
LPVOID lpAddress,
SIZE_T dwSize,
DWORD dwFreeType
);
参数:
- lpAddress:要释放的虚拟内存块的地址。
- dwSize:要释放的虚拟内存块的大小,以字节为单位。
- dwFreeType:指定如何释放虚拟内存块的类型。可以是以下值之一:
- MEM_DECOMMIT:将内存块的状态设置为“未分配”,并将其从提交的内存中移除。
- MEM_RELEASE:将内存块的状态设置为“未分配”,并将其从提交的内存和保留的内存中移除。
返回值:
如果函数成功,则返回非零值。如果函数失败,则返回零。要获取扩展的错误信息,请调用 GetLastError。
示例:
下面是一个使用 VirtualFree 释放虚拟内存的示例代码:
#include <windows.h>
int main() {
// 分配一个大小为 1024 字节的虚拟内存块
LPVOID p = VirtualAlloc(NULL, 1024, MEM_COMMIT, PAGE_READWRITE);
if (p == NULL) {
// 分配内存失败
return -1;
}
// 释放虚拟内存
VirtualFree(p, 0, MEM_RELEASE);
return 0;
}
上面的代码首先使用 VirtualAlloc 分配了一个大小为 1024 字节的虚拟内存块,然后使用 VirtualFree 释放这个内存块。
注意:使用 VirtualFree 释放的虚拟内存块可能不会立即被系统回收,它可能会保留在内存中一段时间。如果希望立即释放内存,可以使用 VirtualFreeEx 函数,它可以立即释放指定的虚拟内存块。
另外,使用 VirtualFree 释放虚拟内存块之前,应该先使用 VirtualProtect 函数将内存块的访问权限设置为可写,否则可能会导致访问冲突。
#include <windows.h>
int main() {
// 分配一个大小为 1024 字节的虚拟内存块
LPVOID p = VirtualAlloc(NULL, 1024, MEM_COMMIT, PAGE_READWRITE);
if (p == NULL) {
// 分配内存失败
return -1;
}
// 修改内存块的访问权限为可写
DWORD oldProtect;
VirtualProtect(p, 1024, PAGE_READWRITE, &oldProtect);
// 释放虚拟内存
VirtualFree(p, 0, MEM_RELEASE);
return 0;
}
上面的代码首先使用 VirtualAlloc 分配了一个大小为 1024 字节的虚拟内存块,然后使用 VirtualProtect 将内存块的访问权限设置为可写,最后使用 VirtualFree 释放了这个内存块。
注意:使用 VirtualFree 释放虚拟内存块之前,应该先使用 VirtualProtect 函数将内存块的访问权限设置为可写,否则可能会导致访问冲突。
三、VirtualCopy
VirtualCopy 是 Windows 系统的内存管理函数,用于复制虚拟内存块。
语法:
BOOL VirtualCopy(
LPVOID lpvDest,
LPVOID lpvSrc,
SIZE_T cbSize,
DWORD fdwProtect
);
参数:
- lpvDest:要复制到的虚拟内存块的地址。
- lpvSrc:要复制的虚拟内存块的地址。
- cbSize:要复制的虚拟内存块的大小,以字节为单位。
- fdwProtect:指定要复制到的虚拟内存块的访问权限。可以是以下值之一:
- PAGE_EXECUTE:执行访问
- PAGE_EXECUTE_READ:执行和读取访问
- PAGE_EXECUTE_READWRITE:执行、读取和写入访问
- PAGE_EXECUTE_WRITECOPY:执行、写入和复制访问
- PAGE_NOACCESS:没有访问
- PAGE_READONLY:只读访问
- PAGE_READWRITE:读取和写入访问
- PAGE_WRITECOPY:写入和复制访问
返回值:
如果函数成功,则返回非零值。如果函数失败,则返回零。要获取扩展的错误信息,请调用 GetLastError。
示例:
下面是一个使用 VirtualCopy 复制虚拟内存块的示例代码:
#include <windows.h>
#include <iostream>
int main() {
// 分配一个大小为 1024 字节的虚拟内存块
LPVOID pSrc = VirtualAlloc(NULL, 1024, MEM_COMMIT, PAGE_READWRITE);
if (pSrc == NULL) {
// 分配内存失败
return -1;
}
// 向 pSrc 指向的内存块中写入数据
int* q = (int*)pSrc;
*q = 123;
// 分配另一个大小为 1024 字节的虚拟内存块
LPVOID pDest = VirtualAlloc(NULL, 1024, MEM_COMMIT, PAGE_READWRITE);
if (pDest == NULL) {
// 分配内存失败
return -1;
}
// 复制 pSrc 指向的内存块到 pDest 指向的内存块
if (!VirtualCopy(pDest, pSrc, 1024, PAGE_READWRITE)) {
// 复制失败
return -1;
}
// 读取 pDest 指向的内存块中的数据
int* r = (int*)pDest;
std::cout << *r << std::endl;
// 释放虚拟内存
VirtualFree(pSrc, 0, MEM_RELEASE);
VirtualFree(pDest, 0, MEM_RELEASE);
return 0;
}
上面的代码首先使用 VirtualAlloc 分配了两个大小为 1024 字节的虚拟内存块,并指定了这两个内存块的访问权限为读写。然后,代码将第一个内存块中的第一个字节写入了整数 123。接下来,使用 VirtualCopy 将第一个内存块复制到第二个内存块,并指定复制后的内存块的访问权限为读写。最后,代码读取了第二个内存块中的数据,并使用 VirtualFree 释放了两个内存块。
使用 VirtualCopy 复制虚拟内存块时,要注意源内存块和目标内存块的访问权限应该是相同的。如果访问权限不同,则可能会导致访问冲突。
另外,使用 VirtualCopy 复制虚拟内存块时,如果目标内存块已经存在,则会覆盖目标内存块中的原有数据。
还有,使用 VirtualCopy 复制虚拟内存块时,应该注意复制的内存块大小不能超过系统所允许的最大值。如果复制的内存块大小超过了系统所允许的最大值,则会导致 VirtualCopy 失败,并返回错误代码 ERROR_NOT_ENOUGH_MEMORY。
总的来说,VirtualCopy 函数是一个非常有用的内存管理工具,在开发应用程序时可以使用它来复制虚拟内存块,从而提高程序的效率。
四、VirtualQuery
VirtualQuery 是 Windows 系统的内存管理函数,用于查询虚拟内存的信息。
语法:
SIZE_T VirtualQuery(
LPCVOID lpAddress,
PMEMORY_BASIC_INFORMATION lpBuffer,
SIZE_T dwLength
);
参数:
- lpAddress:要查询的虚拟内存块的地址。
- lpBuffer:指向一个 MEMORY_BASIC_INFORMATION 结构体的指针,用于存储虚拟内存信息。
- dwLength:lpBuffer 缓冲区的大小,以字节为单位。
返回值:
如果函数成功,则返回查询到的虚拟内存块的大小,以字节为单位。如果函数失败,则返回零。要获取扩展的错误信息,请调用 GetLastError。
下面是一个使用 VirtualQuery 查询虚拟内存的示例代码:
#include <windows.h>
#include <iostream>
int main() {
// 分配一个大小为 1024 字节的虚拟内存块
LPVOID p = VirtualAlloc(NULL, 1024, MEM_COMMIT, PAGE_READWRITE);
if (p == NULL) {
// 分配内存失败
return -1;
}
// 定义存储虚拟内存信息的结构体
MEMORY_BASIC_INFORMATION mbi;
// 查询虚拟内存信息
SIZE_T size = VirtualQuery(p, &mbi, sizeof(mbi));
if (size == 0) {
// 查询失败
return -1;
}
// 输出虚拟内存信息
std::cout << "虚拟内存块的基地址:" << mbi.BaseAddress << std::endl;
std::cout << "虚拟内存块的大小:" << mbi.RegionSize << std::endl;
std::cout << "虚拟内存块的状态:" << mbi.State << std::endl;
std::cout << "虚拟内存块的保护级别:" << mbi.Protect << std::endl;
std::cout << "虚拟内存块的访问类型:" << mbi.Type << std::endl;
// 释放虚拟内存
VirtualFree(p, 0, MEM_RELEASE);
return 0;
}
上面的代码首先使用 VirtualAlloc 分配了一个大小为 1024 字节的虚拟内存块,然后使用 VirtualQuery 查询了这个内存块的信息。最后,代码使用 VirtualFree 释放了这个内存块。 注意:使用 VirtualQuery 查询虚拟内存信息时,要注意传入的 lpBuffer 缓冲区的大小应该足够存下查询到的虚拟内存信息。如果 lpBuffer 缓冲区的大小不足以存储查询到的信息,则 VirtualQuery 函数会失败,并返回错误代码 ERROR_INSUFFICIENT_BUFFER。
另外,使用 VirtualQuery 查询虚拟内存信息时,应该注意传入的 lpAddress 参数必须是虚拟内存块的基地址。如果 lpAddress 参数不是虚拟内存块的基地址,则 VirtualQuery 函数会失败,并返回错误代码 ERROR_INVALID_PARAMETER。
总的来说,VirtualQuery 函数是一个非常有用的内存管理工具,在开发应用程序时可以使用它来查询虚拟内存的信息,从而更好地管理内存资源。
五、总结
vitualalloc系列的函数实现了windows进程虚拟内存向物理内存的转换,是比较底层和重要的api。还有一些,这些大家自己去找吧。夜已深,好梦。
最后
以上就是甜美板凳为你收集整理的VirtualAlloc|VirtualFree|VirtualCopy |VirtualProtect |VirtualQuery - WINDOWS API 第七弹 为进程申请虚拟内存一、虚拟地址与物理地址二、VirtualAlloc |VirtualFree三、VirtualCopy四、VirtualQuery 五、总结的全部内容,希望文章能够帮你解决VirtualAlloc|VirtualFree|VirtualCopy |VirtualProtect |VirtualQuery - WINDOWS API 第七弹 为进程申请虚拟内存一、虚拟地址与物理地址二、VirtualAlloc |VirtualFree三、VirtualCopy四、VirtualQuery 五、总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复