C++中的多平台内存使用
C++中的多平台内存使用
曾经我有一项任务,需要从一个运行中的应用程序内部确定以下性能参数:
- 可用的虚拟内存总量
- 当前使用的虚拟内存
- 当前由我的进程使用的虚拟内存
- 可用的总内存
- 当前使用的内存
- 当前由我的进程使用的内存
- 当前使用的 CPU 百分比
- 当前由我的进程使用的 CPU 百分比
这些代码需要在 Windows 和 Linux 上运行。尽管这似乎是标准任务,但在手册(WIN32 API、GNU 文档)和互联网上找到必要的信息却花了我几天时间,因为这个主题上有太多不完整/不正确/过期的信息。
为了防止其他人重蹈覆辙,我认为将所有分散的信息以及我通过试错发现的信息收集在这里是一个好主意。
Mac OS X
总虚拟内存
在Mac OS X中,这个有点棘手,因为它不像Linux那样使用预设的交换分区或文件。以下是苹果文档中的一个条目:
注意:与大多数基于Unix的操作系统不同,Mac OS X不使用预分配的交换分区作为虚拟内存。相反,它使用机器启动分区上的所有可用空间。
因此,如果您想知道还有多少可用的虚拟内存,您需要获取根分区的大小。您可以像这样实现:
struct statfs stats; if (0 == statfs("/", &stats)) { myFreeSwap = (uint64_t)stats.f_bsize * stats.f_bfree; }
当前已使用的虚拟内存总量
使用“vm.swapusage”键调用systcl可提供有关交换使用情况的有趣信息:
sysctl -n vm.swapusage vm.swapusage: total = 3072.00M used = 2511.78M free = 560.22M (encrypted)
请注意,如果需要更多的交换,则此处显示的总交换使用情况可能会发生变化,如上一节所述。因此,总数实际上是当前交换总数。在C++中,可以通过以下方式查询这些数据:
xsw_usage vmusage = {0}; size_t size = sizeof(vmusage); if( sysctlbyname("vm.swapusage", &vmusage, &size, NULL, 0)!=0 ) { perror( "unable to get swap usage by calling sysctlbyname(\"vm.swapusage\",...)" ); }
请注意,sysctl.h中声明的“xsw_usage”似乎没有文档记录,我怀疑有一种更便携的访问这些值的方式。
进程当前使用的虚拟内存
您可以使用task_info
函数获取有关当前进程的统计信息。这包括您的进程的当前住址大小和当前虚拟大小。
#includestruct task_basic_info t_info; mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; if (KERN_SUCCESS != task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count)) { return -1; } // resident size is in t_info.resident_size; // virtual size is in t_info.virtual_size;
可用的总RAM
您的系统中可用的物理RAM量可使用sysctl
系统函数获取,如下所示:
#include#include ... int mib[2]; int64_t physical_memory; mib[0] = CTL_HW; mib[1] = HW_MEMSIZE; length = sizeof(int64_t); sysctl(mib, 2, &physical_memory, &length, NULL, 0);
当前已使用的RAM
您可以从host_statistics
系统函数中获取一般内存统计信息。
#include#include #include #include int main(int argc, const char * argv[]) { vm_size_t page_size; mach_port_t mach_port; mach_msg_type_number_t count; vm_statistics64_data_t vm_stats; mach_port = mach_host_self(); count = sizeof(vm_stats) / sizeof(natural_t); if (KERN_SUCCESS == host_page_size(mach_port, &page_size) && KERN_SUCCESS == host_statistics64(mach_port, HOST_VM_INFO, (host_info64_t)&vm_stats, &count)) { long long free_memory = (int64_t)vm_stats.free_count * (int64_t)page_size; long long used_memory = ((int64_t)vm_stats.active_count + (int64_t)vm_stats.inactive_count + (int64_t)vm_stats.wire_count) * (int64_t)page_size; printf("free memory: %lld\nused memory: %lld\n", free_memory, used_memory); } return 0; }
这里需要注意的一件事是,在Mac OS X中有五种类型的内存页面。它们如下:
- 被锁定在原处且无法交换出去的有线页面。
- 正在加载到物理内存中且相对难以退出的活动页面。
- 已加载到内存中,但最近没有使用过,甚至可能根本不需要的非活动页面。这些是潜在的交换候选项。这些内存可能需要清除。
- 已经以某种方式缓存的缓存页面,很可能会被轻松地重复使用。缓存内存可能不需要清除。仍然有可能重新激活缓存页面。
- 完全空闲并可以使用的自由页面。
请注意,即使Mac OS X有时显示实际可用内存非常少,这也可能并不是短时间内有多少可立即使用的好指示。
进程当前使用的RAM
请参阅上面的“进程当前使用的虚拟内存”。同样的代码适用。
Windows
其中一些值可以通过相应的Win32 API轻松获取,我在这里列出它们是为了完整性。其他值需要从性能数据助手库(PDH)中获取,这有点“不直观”,需要做很多痛苦的尝试和错误才能让它正常工作。(至少对我来说,我可能只是有点愚蠢...)
注意:以下代码已省略所有错误检查,确确实实应该检查返回代码...!
-
虚拟内存总量:
#include "windows.h" MEMORYSTATUSEX memInfo; memInfo.dwLength = sizeof(MEMORYSTATUSEX); GlobalMemoryStatusEx(&memInfo); DWORDLONG totalVirtualMem = memInfo.ullTotalPageFile;
注意:这里的名称“TotalPageFile”有点误导。实际上此参数给出的是“虚拟内存大小”,即交换文件大小加上已安装的RAM。
-
当前使用的虚拟内存:
与“总虚拟内存”中的代码相同,然后
DWORDLONG virtualMemUsed = memInfo.ullTotalPageFile - memInfo.ullAvailPageFile;
-
当前进程正在使用的虚拟内存:
#include "windows.h" #include "psapi.h" PROCESS_MEMORY_COUNTERS_EX pmc; GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc)); SIZE_T virtualMemUsedByMe = pmc.PrivateUsage;
-
物理内存总量(RAM):
与“总虚拟内存”中的代码相同,然后
DWORDLONG totalPhysMem = memInfo.ullTotalPhys;
-
当前使用的物理内存:
与“总虚拟内存”中的代码相同,然后
DWORDLONG physMemUsed = memInfo.ullTotalPhys - memInfo.ullAvailPhys;
-
当前进程正在使用的物理内存:
与“当前进程正在使用的虚拟内存”中的代码相同,然后
SIZE_T physMemUsedByMe = pmc.WorkingSetSize;
-
当前正在使用的CPU:
#include "TCHAR.h" #include "pdh.h" static PDH_HQUERY cpuQuery; static PDH_HCOUNTER cpuTotal; void init(){ PdhOpenQuery(NULL, NULL, &cpuQuery); // You can also use L"\\Processor(*)\\% Processor Time" and get individual CPU values with PdhGetFormattedCounterArray() PdhAddEnglishCounter(cpuQuery, L"\\Processor(_Total)\\% Processor Time", NULL, &cpuTotal); PdhCollectQueryData(cpuQuery); } double getCurrentValue(){ PDH_FMT_COUNTERVALUE counterVal; PdhCollectQueryData(cpuQuery); PdhGetFormattedCounterValue(cpuTotal, PDH_FMT_DOUBLE, NULL, &counterVal); return counterVal.doubleValue; }
-
当前进程正在使用的CPU:
#include "windows.h" static ULARGE_INTEGER lastCPU, lastSysCPU, lastUserCPU; static int numProcessors; static HANDLE self; void init(){ SYSTEM_INFO sysInfo; FILETIME ftime, fsys, fuser; GetSystemInfo(&sysInfo); numProcessors = sysInfo.dwNumberOfProcessors; GetSystemTimeAsFileTime(&ftime); memcpy(&lastCPU, &ftime, sizeof(FILETIME)); self = GetCurrentProcess(); GetProcessTimes(self, &ftime, &ftime, &fsys, &fuser); memcpy(&lastSysCPU, &fsys, sizeof(FILETIME)); memcpy(&lastUserCPU, &fuser, sizeof(FILETIME)); } double getCurrentValue(){ FILETIME ftime, fsys, fuser; ULARGE_INTEGER now, sys, user; double percent; GetSystemTimeAsFileTime(&ftime); memcpy(&now, &ftime, sizeof(FILETIME)); GetProcessTimes(self, &ftime, &ftime, &fsys, &fuser); memcpy(&sys, &fsys, sizeof(FILETIME)); memcpy(&user, &fuser, sizeof(FILETIME)); percent = (sys.QuadPart - lastSysCPU.QuadPart) + (user.QuadPart - lastUserCPU.QuadPart); percent /= (now.QuadPart - lastCPU.QuadPart); percent /= numProcessors; lastCPU = now; lastUserCPU = user; lastSysCPU = sys; return percent * 100; }
Linux
在Linux上,最初似乎很明显的选择是使用类POSIX的API(如getrusage()
等)。我花了一些时间努力使其工作,但从来没有得到有意义的值。最后当我检查内核源代码时,我发现似乎这些API在Linux内核2.6版本中还没有完全实现?
最后我通过读取伪文件系统/proc
和内核调用来获得所有的值。
-
虚拟内存总量:
#include "sys/types.h" #include "sys/sysinfo.h" struct sysinfo memInfo; sysinfo (&memInfo); long long totalVirtualMem = memInfo.totalram; //Add other values in next statement to avoid int overflow on right hand side... totalVirtualMem += memInfo.totalswap; totalVirtualMem *= memInfo.mem_unit;
-
当前使用的虚拟内存:
与“总虚拟内存”中的代码相同,然后
long long virtualMemUsed = memInfo.totalram - memInfo.freeram; //Add other values in next statement to avoid int overflow on right hand side... virtualMemUsed += memInfo.totalswap - memInfo.freeswap; virtualMemUsed *= memInfo.mem_unit;
-
当前进程正在使用的虚拟内存:
#include "stdlib.h" #include "stdio.h" #include "string.h" int parseLine(char* line){ // This assumes that a digit will be found and the line ends in " Kb". int i = strlen(line); const char* p = line; while (*p <'0' || *p > '9') p++; line[i-3] = '\0'; i = atoi(p); return i; } int getValue(){ //Note: this value is in KB! FILE* file = fopen("/proc/self/status", "r"); int result = -1; char line[128]; while (fgets(line, 128, file) != NULL){ if (strncmp(line, "VmSize:", 7) == 0){ result = parseLine(line); break; } } fclose(file); return result; }
-
物理内存总量(RAM):
与“总虚拟内存”中的代码相同,然后
long long totalPhysMem = memInfo.totalram; //Multiply in next statement to avoid int overflow on right hand side... totalPhysMem *= memInfo.mem_unit;
-
当前使用的物理内存:
与“总虚拟内存”中的代码相同,然后
long long physMemUsed = memInfo.totalram - memInfo.freeram; //Multiply in next statement to avoid int overflow on right hand side... physMemUsed *= memInfo.mem_unit;
-
当前进程正在使用的物理内存:
将“当前进程正在使用的虚拟内存”中的getValue()更改为以下内容:
int getValue(){ //Note: this value is in KB! FILE* file = fopen("/proc/self/status", "r"); int result = -1; char line[128]; while (fgets(line, 128, file) != NULL){ if (strncmp(line, "VmRSS:", 6) == 0){ result = parseLine(line); break; } } fclose(file); return result; }
-
当前正在使用的CPU:
#include "stdlib.h" #include "stdio.h" #include "string.h" static unsigned long long lastTotalUser, lastTotalUserLow, lastTotalSys, lastTotalIdle; void init(){ FILE* file = fopen("/proc/stat", "r"); fscanf(file, "cpu %llu %llu %llu %llu", &lastTotalUser, &lastTotalUserLow, &lastTotalSys, &lastTotalIdle); fclose(file); } double getCurrentValue(){ double percent; FILE* file; unsigned long long totalUser, totalUserLow, totalSys, totalIdle, total; file = fopen("/proc/stat", "r"); fscanf(file, "cpu %llu %llu %llu %llu", &totalUser, &totalUserLow, &totalSys, &totalIdle); fclose(file); if (totalUser < lastTotalUser || totalUserLow < lastTotalUserLow || totalSys < lastTotalSys || totalIdle < lastTotalIdle){ //Overflow detection. Just skip this value. percent = -1.0; } else{ total = (totalUser - lastTotalUser) + (totalUserLow - lastTotalUserLow) + (totalSys - lastTotalSys); percent = total; total += (totalIdle - lastTotalIdle); percent /= total; percent *= 100; } lastTotalUser = totalUser; lastTotalUserLow = totalUserLow; lastTotalSys = totalSys; lastTotalIdle = totalIdle; return percent; }
-
当前进程正在使用的CPU:
#include "stdlib.h" #include "stdio.h" #include "string.h" #include "sys/times.h" #include "sys/vtimes.h" static clock_t lastCPU, lastSysCPU, lastUserCPU; static int numProcessors; void init(){ FILE* file; struct tms timeSample; char line[128]; lastCPU = times(&timeSample); lastSysCPU = timeSample.tms_stime; lastUserCPU = timeSample.tms_utime; file = fopen("/proc/cpuinfo", "r"); numProcessors = 0; while(fgets(line, 128, file) != NULL){ if (strncmp(line, "processor", 9) == 0) numProcessors++; } fclose(file); } double getCurrentValue(){ struct tms timeSample; clock_t now; double percent; now = times(&timeSample); if (now <= lastCPU || timeSample.tms_stime < lastSysCPU || timeSample.tms_utime < lastUserCPU){ //Overflow detection. Just skip this value. percent = -1.0; } else{ percent = (timeSample.tms_stime - lastSysCPU) + (timeSample.tms_utime - lastUserCPU); percent /= (now - lastCPU); percent /= numProcessors; percent *= 100; } lastCPU = now; lastSysCPU = timeSample.tms_stime; lastUserCPU = timeSample.tms_utime; return percent; }
TODO:其他平台
我认为,一些Linux代码对Unix也适用,除了读取/proc伪文件系统的部分。也许在Unix上,可以用getrusage()
等函数替换这些部分?
无法进行翻译,因为 "
" 标签不是一个可翻译的文本内容。