《Windows系统编程》——内存基础与相关结构 笔记

课堂笔记

WINAPI学习

在这堂课也有点摸清楚一些规律了,WINAPI 中的一些结构体,除了定义结构体本身以外还会定义它的指针类型,一般在该变量类型名前加上前缀 lp(LP)。

GetSystemInfo

GetSystemInfo 函数用于获取系统信息,传入参数位 SYSTEM_INFO *,没有返回值,获取的系统信息通过 SYSTEM_INFO 结构体返回,结构体定义如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
typedef struct _SYSTEM_INFO {
union {
DWORD dwOemId; // Obsolete field...do not use
struct {
WORD wProcessorArchitecture;
WORD wReserved;
} DUMMYSTRUCTNAME;
} DUMMYUNIONNAME;
DWORD dwPageSize;
LPVOID lpMinimumApplicationAddress;
LPVOID lpMaximumApplicationAddress;
DWORD_PTR dwActiveProcessorMask;
DWORD dwNumberOfProcessors;
DWORD dwProcessorType;
DWORD dwAllocationGranularity;
WORD wProcessorLevel;
WORD wProcessorRevision;
} SYSTEM_INFO, *LPSYSTEM_INFO;

其中

  • dwPageSize 为内存页大小
  • lpMinimumApplicationAddress 为进程可用的最小内存地址
  • lpMaximumApplicationAddress 为进程可用的最大内存地址

...

我们可以通过调试手段方便地查看结构体各个成员

MemoryStatusEx

MemoryStatusEx 函数用于获取当前电脑内存信息。传入参数类型为 LPMEMORYSTATUSEX,也就是 MEMORYSTATUSEX * 类型。这个类型的结构体定义如下:

1
2
3
4
5
6
7
8
9
10
11
typedef struct _MEMORYSTATUSEX {
DWORD dwLength;
DWORD dwMemoryLoad;
DWORDLONG ullTotalPhys;
DWORDLONG ullAvailPhys;
DWORDLONG ullTotalPageFile;
DWORDLONG ullAvailPageFile;
DWORDLONG ullTotalVirtual;
DWORDLONG ullAvailVirtual;
DWORDLONG ullAvailExtendedVirtual;
} MEMORYSTATUSEX, *LPMEMORYSTATUSEX;

除了 dwLength 外,第一个参数就是 dwMemoryLoad,它告诉了我们当前物理内存占用情况,也就是我们任务管理器看到的内存占用百分比,写一个程序输出就会发现它与任务管理器上显示的信息一致。

1
2
3
4
5
6
7
8
9
10
#include<windows.h>
#include<stdio.h>
int main() {
SYSTEM_INFO info = { 0 };
GetSystemInfo(&info);
printf("PAGESIZE:%d\n", info.dwPageSize);
MEMORYSTATUSEX MemoryInfo = { sizeof(MEMORYSTATUSEX) };
GlobalMemoryStatusEx(&MemoryInfo);
printf("Memory Status: %d%%\n", MemoryInfo.dwMemoryLoad);
}

运行结果:

剩下的成员

  • ullTotalPhys:实际物理内存量
  • ullAvailPhys:可使用的物理内存量
  • ullTotalPageFile:当前系统已提交的内存限制
  • ullAvailPageFile:当前系统可以提交的最大内存量
  • ullTotalVirtual:调用进程的虚拟空间的用户内存大小
  • ullAvailVirtual:当前用户空间没有提交的内存
  • ullAvailExtendedVirtual:保留,始终为0

还是用以上的方式去获取结构信息,调试查看结构体成员。

GetProcessMemoryInfo

用于获取进程的一个内存信息,用到这个 api 需要包含 Psapi.h。第一个参数为 HANDLE 类型,也就是我们获取的进程的句柄,第二个参数是一个结构体,用于接收返回的信息,第三个参数是该结构体的大小。

  • PeakWorkingSetSize 是该进程创建以来使用的内存大小(peak表示顶峰)。
  • WorkingSetSize 是该进程当前使用的内存大小。

那么我们获取当前进程所使用的内存信息就像下面一样获取。

1
2
PROCESS_MEMORY_COUNTERS psmemCounters;
GetProcessMemoryInfo(GetCurrentProcess(), &psmemCounters, sizeof(PROCESS_MEMORY_COUNTERS));

VirtualAlloc

用于为本进程分配内存。第一个参数为分配内存的地址,填 NULL 表示让操作系统自己分配,第二个参数就是我们要分配的内存的大小,第三个参数是内存的属性,一般我们传入 MEM_COMMIT 即可,它的作用是为指定的保留内存页面分配内存费用(来自内存的总大小和磁盘上的页面文件)该函数还保证当调用者稍后最初访问内存时,内容将为零,除非/直到实际访问虚拟地址,否则不会分配实际的物理页面,第四个参数是内存的保护属性,就是读写执行那些的,实际上它有以下那么多属性

返回值为一个地址指针。

VirtualFree

用于为本进程释放内存,第一个参数为释放内存的起始地址,第二个参数为 size,但是通常情况下我们只有传 0 才能成功释放,传其他值不能够释放。第三个参数我们就给 MEM_REALEASE,表示释放这个内存。

其实释放这个内存的函数,两个参数都是固定的。

关于这个参数的解释可以去看 msdn,有详细说明。

VirtualQuery

查询内存页面的属性,第一个参数为内存地址,第二个参数为接受内存信息的结构体指针,第三个参数为结构体大小。

结构体定义如下

1
2
3
4
5
6
7
8
9
10
11
12
typedef struct _MEMORY_BASIC_INFORMATION {
PVOID BaseAddress;
PVOID AllocationBase;
DWORD AllocationProtect;
#if defined (_WIN64)
WORD PartitionId;
#endif
SIZE_T RegionSize;
DWORD State;
DWORD Protect;
DWORD Type;
} MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;
  • BaseAddress:内存基地址
  • AllocationBase:申请区域的基地址
  • AllocationProtect:内存保护属性,读写执行那些的
  • RegionSize:区域的大小
  • State:页面的状态
  • Protect:区域中页面的访问保护
  • Type:页面类型

VirtualProtect

更改内存页保护属性,第一个参数为该内存页地址,第二个参数为大小,第三个参数为要设置的保护属性,第四个参数用于接收该内存页原属性。

总结

以上 Virtual 开头的 api 均有 Ex 模式, Ex 模式表示在别的线程当中进行对应的操作。不过事先我们要先获取进程句柄,并且 给上 PROCESS_ALL_ACCESS 的权限。

这节课也就是学习了一些内存相关 API 的使用,算打点基础吧,本节课写的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<windows.h>
#include<stdio.h>
#include<Psapi.h>
int main() {
SYSTEM_INFO info = { 0 };
GetSystemInfo(&info);
printf("PAGESIZE:%d\n", info.dwPageSize);
MEMORYSTATUSEX MemoryInfo = { sizeof(MEMORYSTATUSEX) };
GlobalMemoryStatusEx(&MemoryInfo);
printf("Memory Status: %d%%\n", MemoryInfo.dwMemoryLoad);
PROCESS_MEMORY_COUNTERS psmemCounters;
GetProcessMemoryInfo(GetCurrentProcess(), &psmemCounters, sizeof(PROCESS_MEMORY_COUNTERS));

LPVOID buffer=VirtualAlloc(NULL, 0x1000, MEM_COMMIT, PAGE_READWRITE);
if (buffer == NULL) {
printf("alloc fail");
exit(0);
}
RtlZeroMemory(buffer, 0x1000);
VirtualFree(buffer, 0, MEM_RELEASE);
MEMORY_BASIC_INFORMATION lpbuffer = { 0 };
VirtualQuery(buffer, &lpbuffer, sizeof(MEMORY_BASIC_INFORMATION));
system("pause");
}