如何在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 总量
可以使用如下所示的 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 中有五种类型的内存页面。它们如下所示:
- Wired 页面被锁定在一个位置,不能被交换出。
- Active 页面正在加载到物理内存中,相对较难被交换出。
- Inactive 页面加载到内存中,但最近没有被使用,甚至可能根本不需要。这些是潜在的置换对象。这个内存可能需要刷新。
- Cached 页面已经缓存了一些将来可能很容易重新使用的东西。缓存内存可能不需要刷新。缓存页面仍然可以被重新激活。
- Free 页面完全为空闲,可以随时使用。
需要注意的是,即使 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。
-
当前使用的虚拟内存:
同“Total Virtual Memory” 中的代码,然后
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):
同“Total Virtual Memory” 中的代码,然后
DWORDLONG totalPhysMem = memInfo.ullTotalPhys;
-
当前使用的物理内存:
同“Total Virtual Memory” 中的代码,然后
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()
等。我花了一些时间试图让这些工作,但从来没有得到有意义的值。最终我通过同时读取伪文件系统/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;
-
当前使用的虚拟内存:
同“Total Virtual Memory” 中的代码,然后
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):
同“Total Virtual Memory” 中的代码,然后
long long totalPhysMem = memInfo.totalram; //Multiply in next statement to avoid int overflow on right hand side... totalPhysMem *= memInfo.mem_unit;
-
当前使用的物理内存:
同“Total Virtual Memory” 中的代码,然后
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()
和类似的函数代替?
此内容无法翻译,因为它只是HTML代码中的闭合标签。