如何在Windows上使用C++获取系统CPU / RAM使用情况
如何在Windows上使用C++获取系统CPU / RAM使用情况
我曾负责在运行的应用程序内部确定以下性能参数:
- 可用的总虚拟内存
- 当前使用的虚拟内存
- 当前由我的进程使用的虚拟内存
- 可用的总 RAM
- 当前使用的 RAM
- 当前由我的进程使用的 RAM
- 当前使用的 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
使用sysctl
系统函数可获取系统中可用的物理RAM量,方法如下:
#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中获取,我在这里只是为了完整性而列出它们。其他一些值需要从Performance Data Helper库(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下,一开始看起来显然要使用类似于getrusage()
等的POSIX API。我花了一些时间尝试让它正常工作,但从未得到有意义的值。最后我查看了内核源代码时发现,显然这些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:其他平台
我认为同样适用于Unix的一部分Linux代码,除了读取/proc伪文件系统的部分。也许在Unix上,这些部分可以由getrusage()
和类似的函数替换吗?
这是无效的HTML标记,它将被忽略并显示为文本"
"。没有具体的含义或功能。