如何在C++中了解给定进程消耗的资源数量?

20 浏览
0 Comments

如何在C++中了解给定进程消耗的资源数量?

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

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


  • 可用的总内存
  • 当前正在使用的内存
  • 我的进程当前正在使用的内存


  • 当前使用的% CPU
  • 我的进程当前使用的% CPU

代码必须在Windows和Linux上运行。即使这似乎是一个标准任务,在手册(WIN32 API,GNU文档)以及在互联网上找到必要的信息也花费了我好几天的时间,因为有太多不完整/不正确/过时的信息在那里。

为了避免其他人遇到同样的问题,我想将所有分散的信息以及我通过试错找到的信息收集在此处。

admin 更改状态以发布 2023年5月23日
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. Wired 页面被锁定在一个位置,不能被交换出。
  2. Active 页面正在加载到物理内存中,相对较难被交换出。
  3. Inactive 页面加载到内存中,但最近没有被使用,甚至可能根本不需要。这些是潜在的置换对象。这个内存可能需要刷新。
  4. Cached 页面已经缓存了一些将来可能很容易重新使用的东西。缓存内存可能不需要刷新。缓存页面仍然可以被重新激活。
  5. Free 页面完全为空闲,可以随时使用。

需要注意的是,即使 Mac OS X 有时显示实际空闲内存很少,这也不一定是短期内可以使用的多少的好指示。

进程当前使用的 RAM

请参阅上面的 "进程当前使用的虚拟内存"。适用相同的代码。

0
0 Comments

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代码中的闭合标签。

0