如何从进程内部确定CPU和内存消耗

40 浏览
0 Comments

如何从进程内部确定CPU和内存消耗

我曾经有一个任务,需要在一个运行中的应用程序中确定以下性能参数:

  • 总可用虚拟内存
  • 当前使用的虚拟内存
  • 由我的进程当前使用的虚拟内存


  • 总可用RAM
  • 当前使用的RAM
  • 由我的进程当前使用的RAM


  • 当前使用的CPU百分比
  • 由我的进程当前使用的CPU百分比

这段代码需要在Windows和Linux上运行。尽管这似乎是一个标准的任务,但是找到必要的信息在手册(WIN32 API,GNU docs)以及互联网上花费了我数天时间,因为这个主题上有太多不完整/不正确/过时的信息。

为了帮助其他人免去同样的麻烦,我认为将所有分散的信息以及我通过尝试和错误发现的信息收集到这个地方是个好主意。

admin 更改状态以发布 2023年5月21日
0
0 Comments

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函数获取有关当前进程的统计信息。它包括您的进程的当前驻留大小和当前虚拟大小。

#include
struct 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中有五种类型的内存页面。它们如下:

  1. 已锁定的页面,无法被交换出去。
  2. 活动的页面,正在加载到物理内存中,并且相对难以被交换出去。
  3. 非活动的页面,已经加载到内存中,但最近没有被使用,甚至可能根本不需要。这些页面可能成为交换的候选。这种内存可能需要被清除。
  4. 缓存的页面,已经被某种方式缓存,很可能很容易被重复使用。缓存内存可能不需要清除。缓存页面仍然可能被重新激活。
  5. 空闲的页面,完全空闲并且可以被使用。

需要注意的是,即使在某些时候Mac OS X显示实际可用内存很少,这可能并不是即时使用的好指标。

我的进程当前使用的RAM

参见上面的“我的进程当前使用的虚拟内存”。相同的代码适用。

0
0 Comments

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;
    }
    


待办事项:其他平台

我会假设,除了读取/proc伪文件系统的部分之外,一些Linux代码也可以用于Unix。也许在Unix上这些部分可以由getrusage()和类似函数替换?

抱歉,无法翻译无意义的HTML标记。"

"是不合法的HTML标记,并不包含任何翻译内容。请提供更多上下文和信息,以便我们提供正确的帮助。

0