为了账号安全,请及时绑定邮箱和手机立即绑定

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

/ 猿问

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

慕桂英546537 2019-06-06 10:44:36

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

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

  • 可用虚拟内存总量
  • 当前使用的虚拟内存
  • 我的进程当前使用的虚拟内存
  • 可用内存总数
  • Ram目前使用
  • 我的进程目前使用的RAM
  • 目前使用的CPU百分比
  • 我的进程当前使用的CPU%

代码必须在Windows和Linux上运行。尽管这似乎是一项标准任务,但在手册(Win 32 API,GNU docs)中以及在Internet上找到必要的信息花了我几天的时间,因为关于这个主题有很多不完整/不正确/过时的信息要找出来。

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


查看完整描述

3 回答

?
侃侃无极

上面的一些值很容易从适当的Win 32 API中获得,我只是在这里列出它们的完整性。然而,另一些则需要从性能数据帮助库(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(), 
    &pmc, sizeof(pmc));SIZE_T virtualMemUsedByMe = pmc.PrivateUsage;



  • 总物理内存(RAM):

    与“总虚拟内存”中的代码相同,然后

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

    Same code as in "Total Virtual Memory" and thenDWORDLONG 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()等。我花了一些时间试着让它发挥作用,但却没有得到有意义的价值。当我最终检查内核源代码时,我发现这些API显然还没有在Linux2.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;}

Todo:其他平台

我假设,除了读取/proc伪文件系统的部分之外,一些Linux代码也适用于unix。也许在unix上,这些部件可以由getrusage()还有类似的功能?如果有Unix技术的人可以编辑这个答案并填写详细信息?!


查看完整回答
反对 回复 2019-06-06
?
繁星淼淼

MacOSX

我也希望能为MacOSX找到类似的信息。既然不在这里,我就自己挖出来了。这是我发现的一些东西。如果有人有其他建议的话,我很想听听。

全虚拟存储器

这一点在MacOSX上是很棘手的,因为它不使用预置交换分区或像Linux这样的文件。以下是苹果文档中的一个条目:

注:与大多数基于Unix的操作系统不同,MacOSX不对虚拟内存使用预先分配的交换分区。相反,它使用机器引导分区上的所有可用空间。

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

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

目前使用的全部虚拟

使用“vm.swapage”键调用system提供了有关交换使用的有趣信息:

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_use”似乎没有文档化,我怀疑有一种更可移植的方法来访问这些值。

我的进程当前使用的虚拟内存

可以使用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的数量可以使用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: %lld\nused memory: %lld\n", free_memory, used_memory);
    }

    return 0;}

这里需要注意的一点是,MacOSX中有五种类型的内存页。它们如下:

  1. 有线

    已锁定且不能交换的页。
  2. 主动

    加载到物理内存中且相对较难交换的页面。
  3. 不活动

    加载到内存中但最近没有使用的页面,甚至可能根本不需要。这些都是潜在的交换对象。这个记忆可能需要冲洗一下。
  4. 缓存

    已经缓存了一些很容易被重用的页面。缓存内存可能不需要刷新。缓存页仍有可能被重新激活。
  5. 免费

    完全免费并随时可供使用的页面。

值得注意的是,仅仅因为MacOSX可能很少显示实际的空闲内存,有时它可能并不能很好地指示在短时间内可以使用多少内存。

我的进程目前使用的RAM

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


查看完整回答
反对 回复 2019-06-06
?
芜湖不芜


linux

在Linux中,这些信息可以在/proc文件系统中获得。我不太喜欢使用的文本文件格式,因为每个Linux发行版似乎至少定制了一个重要的文件。快速浏览一下“PS”的来源,就会发现它的混乱之处。


但这里是找到你想要的信息的地方:


/proc/meminfo包含您所查找的系统范围内的大多数信息。在我的系统里,我觉得你对此很感兴趣MemTotal, MemFree, 总金额,和无浪:


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/prostat.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利用率。


同样,您必须定期对该文件进行采样,并计算差异,以确定随着时间的推移进程的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”开头的条目很有趣:


范峰进程使用的最大虚拟内存空间(以kB为单位)(1024字节)。

VmSize进程使用的当前虚拟内存空间(以kB为单位)。在我的例子中,它相当大:651,352 kB,或约636兆字节。

VmRss已映射到进程地址空间或其驻留集大小的内存量。这要小得多(420 296 KB,约410兆字节)。不同之处:我的程序通过mmap()映射了636 MB,但只访问了410 MB,因此分配给它的页面只有410 MB。

我唯一不确定的是当前由我的进程使用的交换空间..我不知道这个是否可用。


查看完整回答
反对 回复 2019-06-06

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信