如何确定进程内的CPU和内存消耗?

我曾经从运行的应用程序中确定以下性能参数:

  • 可用虚拟内存总量
  • 当前使用的虚拟内存
  • 我的进程当前使用的虚拟内存
  • 可用RAM总量
  • 目前使用的RAM
  • 目前我的进程使用的RAM
  • 当前使用的CPU百分比
  • 当前由我的进程使用的CPU
  • 代码必须在Windows和Linux上运行。 尽管这似乎是一项标准任务,但在手册(WIN32 API,GNU文档)以及互联网上查找必要的信息花了我好几天,因为关于此主题的信息太多不完整/不正确/过时在那里发现。

    为了避免别人经历同样的麻烦,我认为在一个地方收集所有分散的信息加上我通过反复试验发现的信息是一个好主意。


    视窗

    上面的一些值很容易从相应的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(), &pmc, sizeof(pmc));
    SIZE_T virtualMemUsedByMe = pmc.PrivateUsage;
    


  • 总物理内存(RAM):

    与“Total Virtual Memory”中的代码相同,然后

    DWORDLONG totalPhysMem = memInfo.ullTotalPhys;
    
  • 目前使用的物理内存:

    Same code as in "Total Virtual Memory" and then
    
    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 kernel 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;
    
  • 当前使用的虚拟内存:

    与“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] = '';
        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:其他平台

    我假设,除了读取/ proc伪文件系统的部分之外,某些Linux代码也适用于Unix。 也许在Unix上,这些部分可以用getrusage()和类似的函数来代替? 如果有人拥有Unix专有技术可以编辑这个答案并填写细节?!


    Mac OS X

    我希望能够为Mac OS X找到类似的信息。 既然不在这里,我就出去自己挖了。 这是我发现的一些事情。 如果有人有任何其他建议,我很乐意听到他们。

    总虚拟内存

    这在Mac OS X上很棘手,因为它不使用预设的交换分区或像Linux这样的文件。 以下是Apple的文档中的一个条目:

    注意:与大多数基于Unix的操作系统不同,Mac OS X不为虚拟内存使用预分配的交换分区。 而是使用机器引导分区上的所有可用空间。

    因此,如果您想知道还有多少虚拟内存可用,则需要获取根分区的大小。 你可以这样做:

    struct statfs stats;
    if (0 == statfs("/", &stats))
    {
        myFreeSwap = (uint64_t)stats.f_bsize * stats.f_bfree;
    }
    

    当前使用的虚拟总数

    使用“vm.swapusage”键调用系统提供有关交换使用情况的有趣信息:

    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<mach/mach.h>
    
    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总量

    系统中可用的物理RAM的数量可以通过使用sysctl系统函数获得,如下所示:

    #include <sys/types.h>
    #include <sys/sysctl.h>
    ...
    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 <mach/vm_statistics.h>
    #include <mach/mach_types.h>
    #include <mach/mach_init.h>
    #include <mach/mach_host.h>
    
    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: %lldnused memory: %lldn", free_memory, used_memory);
        }
    
        return 0;
    }
    

    有一点需要注意的是,Mac OS X中有五种类型的内存页面,如下所示:

  • 有线网页已锁定,无法换出
  • 加载到物理内存中的活动页面将相对难以交换
  • 无效页面被加载到内存中,但最近没有被使用过,甚至根本不需要。 这些是交换的潜在候选人。 这个内存可能需要刷新。
  • 缓存的页面已经被缓存了,可能很容易被重用。 缓存的内存可能不需要刷新。 缓存页面仍然可以重新激活
  • 完全免费且可以使用的免费页面。
  • 值得注意的是,仅仅因为Mac OS X有时可能显示非常少的实际可用内存,它可能并不能很好地表明在短时间内可以使用多少内存。

    目前由我的进程使用的RAM

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


    Linux的

    在Linux中,这些信息在/ proc文件系统中可用。 我不是使用的文本文件格式的忠实粉丝,因为每个Linux发行版似乎至少定制了一个重要文件。 作为'ps'的来源的快速查看揭示了混乱。

    但是,在这里可以找到您所寻找的信息:

    / proc / meminfo包含您寻求的大部分系统信息。 这里看起来像我的系统; 我认为你对MemTotalMemFreeSwapTotalSwapFree感兴趣

    Anderson cxc # more /proc/meminfo
    MemTotal:      4083948 kB
    MemFree:       2198520 kB
    Buffers:         82080 kB
    Cached:        1141460 kB
    SwapCached:          0 kB
    Active:        1137960 kB
    Inactive:       608588 kB
    HighTotal:     3276672 kB
    HighFree:      1607744 kB
    LowTotal:       807276 kB
    LowFree:        590776 kB
    SwapTotal:     2096440 kB
    SwapFree:      2096440 kB
    Dirty:              32 kB
    Writeback:           0 kB
    AnonPages:      523252 kB
    Mapped:          93560 kB
    Slab:            52880 kB
    SReclaimable:    24652 kB
    SUnreclaim:      28228 kB
    PageTables:       2284 kB
    NFS_Unstable:        0 kB
    Bounce:              0 kB
    CommitLimit:   4138412 kB
    Committed_AS:  1845072 kB
    VmallocTotal:   118776 kB
    VmallocUsed:      3964 kB
    VmallocChunk:   112860 kB
    HugePages_Total:     0
    HugePages_Free:      0
    HugePages_Rsvd:      0
    Hugepagesize:     2048 kB
    

    对于CPU利用率,你必须做一些工作。 Linux在系统启动后提供整体CPU利用率; 这可能不是您感兴趣的内容。如果您想知道最后一秒或10秒的CPU利用率,则需要查询信息并自行计算。

    这些信息可以在/ proc / stat中找到 ,在http://www.linuxhowtos.org/System/procstat.htm中有详细记录。 这是它在我的4芯盒子上的样子:

    Anderson cxc #  more /proc/stat
    cpu  2329889 0 2364567 1063530460 9034 9463 96111 0
    cpu0 572526 0 636532 265864398 2928 1621 6899 0
    cpu1 590441 0 531079 265949732 4763 351 8522 0
    cpu2 562983 0 645163 265796890 682 7490 71650 0
    cpu3 603938 0 551790 265919440 660 0 9040 0
    intr 37124247
    ctxt 50795173133
    btime 1218807985
    processes 116889
    procs_running 1
    procs_blocked 0
    

    首先,您需要确定系统中有多少CPU(或处理器或处理内核)可用。 为此,请计算'cpuN'条目的数量,其中N从0开始并递增。 不要将'cpu'行计算在内,这是cpuN行的组合。 在我的例子中,你可以看到cpu0到cpu3,共有4个处理器。 从现在开始,你可以忽略cpu0..cpu3,只关注'cpu'行。

    接下来,您需要知道这些行中的第四个数字是空闲时间的度量,因此'cpu'行中的第四个数字是自启动以来所有处理器的总空闲时间。 这个时间是在Linux“jiffies”中测得的,每秒1/100秒。

    但是你不关心总的空闲时间, 你关心的是特定时期的空闲时间,例如最后一秒。 请计算一下,你需要两次读取这个文件,间隔1秒。然后你可以做一行第四个值的差异。 例如,如果你拿一个样本并得到:

    cpu  2330047 0 2365006 1063853632 9035 9463 96114 0
    

    然后等一秒钟,你会得到这个样本:

    cpu  2330047 0 2365007 1063854028 9035 9463 96114 0
    

    减去这两个数字,得到396的差异,这意味着你的CPU在最后1.00秒内已经空闲了3.96秒。 当然,诀窍是你需要除以处理器的数量。 3.96 / 4 = 0.99,并有你的闲置百分比; 99%闲置,1%繁忙。

    在我的代码中,我有一个360个条目的环形缓冲区,并且我每秒都读这个文件。 这使我可以快速计算1秒,10秒等CPU使用率,最长可达1小时。

    对于特定于进程的信息,您必须查看/ proc / pid ; 如果你不关心你的pid,你可以看看/ proc / self。

    您的进程使用的CPU可用于/ proc / self / stat 。 这是一个奇怪的文件,由单一行组成; 例如:

    19340 (whatever) S 19115 19115 3084 34816 19115 4202752 118200 607 0 0 770 384 2
     7 20 0 77 0 266764385 692477952 105074 4294967295 134512640 146462952 321468364
    8 3214683328 4294960144 0 2147221247 268439552 1276 4294967295 0 0 17 0 0 0 0
    

    这里的重要数据是第13和第14个标记(这里是0和770)。 第13个标记是进程在用户模式下执行的jiffies的数量,第14个是进程在内核模式下执行的jiffies的数量。 将两者一起添加,并获得其总CPU利用率。

    同样,您将不得不定期对此文件进行采样,并计算diff,以便确定进程的CPU使用率。

    编辑:记住,当你计算你的进程的CPU使用率时,你必须考虑到1)进程中的线程数量,以及2)系统中的处理器数量。 例如,如果您的单线程进程只使用25%的CPU,那可能是好的或坏的。 擅长单处理器系统,但在4处理器系统上不好; 这意味着您的进程不断运行,并使用100%的CPU周期。

    对于特定于进程的内存信息,您只需查看/ proc / self / status,如下所示:

    Name:   whatever
    State:  S (sleeping)
    Tgid:   19340
    Pid:    19340
    PPid:   19115
    TracerPid:      0
    Uid:    0       0       0       0
    Gid:    0       0       0       0
    FDSize: 256
    Groups: 0 1 2 3 4 6 10 11 20 26 27
    VmPeak:   676252 kB
    VmSize:   651352 kB
    VmLck:         0 kB
    VmHWM:    420300 kB
    VmRSS:    420296 kB
    VmData:   581028 kB
    VmStk:       112 kB
    VmExe:     11672 kB
    VmLib:     76608 kB
    VmPTE:      1244 kB
    Threads:        77
    SigQ:   0/36864
    SigPnd: 0000000000000000
    ShdPnd: 0000000000000000
    SigBlk: fffffffe7ffbfeff
    SigIgn: 0000000010001000
    SigCgt: 20000001800004fc
    CapInh: 0000000000000000
    CapPrm: 00000000ffffffff
    CapEff: 00000000fffffeff
    Cpus_allowed:   0f
    Mems_allowed:   1
    voluntary_ctxt_switches:        6518
    nonvoluntary_ctxt_switches:     6598
    

    以'Vm'开头的条目是有趣的:

  • VmPeak是进程使用的最大虚拟内存空间,以kB(1024字节)为单位。
  • VmSize是进程使用的当前虚拟内存空间,单位为kB。 在我的例子中,它非常大:651,352 kB或大约636 MB。
  • VmRss是已映射到进程地址空间或其常驻集大小的内存量。 这实际上更小(420,296 kB或约410兆字节)。 区别在于:我的程序通过mmap()映射了636 MB,但只访问了410 MB,因此只有410 MB的页面分配给它。
  • 我不确定的唯一项目是我的进程当前使用的Swapspace 。 我不知道这是否可用。

    链接地址: http://www.djcxy.com/p/13997.html

    上一篇: How to determine CPU and memory consumption from inside a process?

    下一篇: FireFox marketplace in