如何从进程内部确定 CPU 和内存消耗 [英] How to determine CPU and memory consumption from inside a process
问题描述
我曾经负责从正在运行的应用程序内部确定以下性能参数:
I once had the task of determining the following performance parameters from inside a running application:
- 可用的虚拟内存总量
- 当前使用的虚拟内存
- 我的进程当前使用的虚拟内存
- 可用内存总量
- 当前使用的内存
- 我的进程当前使用的 RAM
- 当前使用的 CPU 百分比
- 我的进程当前使用的 CPU 百分比
代码必须在 Windows 和 Linux 上运行.尽管这似乎是一项标准任务,但在手册(WIN32 API、GNU 文档)以及 Internet 上找到必要的信息花了我几天时间,因为关于这个主题的信息太多不完整/不正确/过时在那里发现.
The code had to run on Windows and Linux. Even though this seems to be a standard task, finding the necessary information in the manuals (WIN32 API, GNU docs) as well as on the Internet took me several days, because there's so much incomplete/incorrect/outdated information on this topic to be found out there.
为了避免其他人遇到同样的麻烦,我认为最好将所有分散的信息以及我通过反复试验发现的信息收集到一个地方.
In order to save others from going through the same trouble, I thought it would be a good idea to collect all the scattered information plus what I found by trial and error here in one place.
推荐答案
Windows
上面的一些值可以很容易地从相应的 Win32 API 中获得,为了完整起见,我只是在此处列出它们.然而,其他的需要从性能数据助手库 (PDH) 中获取,这有点不直观".并需要大量痛苦的反复试验才能开始工作.(至少我花了很长时间,也许我只是有点傻......)
Windows
Some of the above values are easily available from the appropriate Win32 API, I just list them here for completeness. Others, however, need to be obtained from the Performance Data Helper library (PDH), which is a bit "unintuitive" and takes a lot of painful trial and error to get to work. (At least it took me quite a while, perhaps I've been only a bit stupid...)
注意:为了清楚起见,以下代码中省略了所有错误检查.请检查返回代码...!
Note: for clarity all error checking has been omitted from the following code. Do check the return codes...!
总虚拟内存:
Total Virtual Memory:
#include "windows.h"
MEMORYSTATUSEX memInfo;
memInfo.dwLength = sizeof(MEMORYSTATUSEX);
GlobalMemoryStatusEx(&memInfo);
DWORDLONG totalVirtualMem = memInfo.ullTotalPageFile;
注意:名称TotalPageFile"这里有点误导.实际上,此参数给出了虚拟内存大小",即交换文件的大小加上已安装的 RAM.
Note: The name "TotalPageFile" is a bit misleading here. In reality this parameter gives the "Virtual Memory Size", which is size of swap file plus installed RAM.
当前使用的虚拟内存:
与总虚拟内存"中的代码相同;然后
Same code as in "Total Virtual Memory" and then
DWORDLONG virtualMemUsed = memInfo.ullTotalPageFile - memInfo.ullAvailPageFile;
当前进程当前使用的虚拟内存:
Virtual Memory currently used by current process:
#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 Physical Memory (RAM):
与总虚拟内存"中的代码相同;然后
Same code as in "Total Virtual Memory" and then
DWORDLONG totalPhysMem = memInfo.ullTotalPhys;
当前使用的物理内存:
Physical Memory currently used:
与总虚拟内存"中的代码相同;然后
Same code as in "Total Virtual Memory" and then
DWORDLONG physMemUsed = memInfo.ullTotalPhys - memInfo.ullAvailPhys;
当前进程当前使用的物理内存:
Physical Memory currently used by current process:
与当前进程当前使用的虚拟内存"中的代码相同;然后
Same code as in "Virtual Memory currently used by current process" and then
SIZE_T physMemUsedByMe = pmc.WorkingSetSize;
当前使用的 CPU:
CPU currently used:
#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:
CPU currently used by current process:
#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 上,一开始似乎很明显的选择是使用 POSIX API,如 getrusage()
等.我花了一些时间试图让它工作,但从未得到有意义的值.当我最终自己检查内核源代码时,我发现这些 API 显然还没有从 Linux 内核 2.6 开始完全实现!?
On Linux the choice that seemed obvious at first was to use the POSIX APIs like getrusage()
etc. I spent some time trying to get this to work, but never got meaningful values. When I finally checked the kernel sources themselves, I found out that apparently these APIs are not yet completely implemented as of Linux kernel 2.6!?
最后,我通过读取伪文件系统 /proc
和内核调用的组合获得了所有值.
In the end I got all values via a combination of reading the pseudo-filesystem /proc
and kernel calls.
总虚拟内存:
Total Virtual Memory:
#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;
当前使用的虚拟内存:
Virtual Memory currently used:
与总虚拟内存"中的代码相同;然后
Same code as in "Total Virtual Memory" and then
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;
当前进程当前使用的虚拟内存:
Virtual Memory currently used by current process:
#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] = ' ';
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 Physical Memory (RAM):
与总虚拟内存"中的代码相同;然后
Same code as in "Total Virtual Memory" and then
long long totalPhysMem = memInfo.totalram;
//Multiply in next statement to avoid int overflow on right hand side...
totalPhysMem *= memInfo.mem_unit;
当前使用的物理内存:
Physical Memory currently used:
与总虚拟内存"中的代码相同;然后
Same code as in "Total Virtual Memory" and then
long long physMemUsed = memInfo.totalram - memInfo.freeram;
//Multiply in next statement to avoid int overflow on right hand side...
physMemUsed *= memInfo.mem_unit;
当前进程当前使用的物理内存:
Physical Memory currently used by current process:
更改当前进程当前使用的虚拟内存"中的 getValue()如下:
Change getValue() in "Virtual Memory currently used by current process" as follows:
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:
CPU currently used:
#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:
CPU currently used by current process:
#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;
}
我认为,除了读取/proc 伪文件系统的部分之外,一些 Linux 代码也适用于 Unix.也许在 Unix 上这些部分可以用 getrusage()
和类似的函数代替?
I would assume, that some of the Linux code also works for the Unixes, except for the parts that read the /proc pseudo-filesystem. Perhaps on Unix these parts can be replaced by getrusage()
and similar functions?
这篇关于如何从进程内部确定 CPU 和内存消耗的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!