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

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

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

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


当前回答

Mac OS X - CPU

整体CPU使用率:

从检索Mac OS X上的系统信息:

#include <mach/mach_init.h>
#include <mach/mach_error.h>
#include <mach/mach_host.h>
#include <mach/vm_map.h>

static unsigned long long _previousTotalTicks = 0;
static unsigned long long _previousIdleTicks = 0;

// Returns 1.0f for "CPU fully pinned", 0.0f for "CPU idle", or somewhere in between
// You'll need to call this at regular intervals, since it measures the load between
// the previous call and the current one.
float GetCPULoad()
{
   host_cpu_load_info_data_t cpuinfo;
   mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
   if (host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, (host_info_t)&cpuinfo, &count) == KERN_SUCCESS)
   {
      unsigned long long totalTicks = 0;
      for(int i=0; i<CPU_STATE_MAX; i++) totalTicks += cpuinfo.cpu_ticks[i];
      return CalculateCPULoad(cpuinfo.cpu_ticks[CPU_STATE_IDLE], totalTicks);
   }
   else return -1.0f;
}

float CalculateCPULoad(unsigned long long idleTicks, unsigned long long totalTicks)
{
  unsigned long long totalTicksSinceLastTime = totalTicks-_previousTotalTicks;
  unsigned long long idleTicksSinceLastTime  = idleTicks-_previousIdleTicks;
  float ret = 1.0f-((totalTicksSinceLastTime > 0) ? ((float)idleTicksSinceLastTime)/totalTicksSinceLastTime : 0);
  _previousTotalTicks = totalTicks;
  _previousIdleTicks  = idleTicks;
  return ret;
}

其他回答

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. conf”调用systcl。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

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

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

这里需要注意的是,在Mac OS x中有五种类型的内存页,它们如下所示:

Wired pages that are locked in place and cannot be swapped out Active pages that are loading into physical memory and would be relatively difficult to swap out Inactive pages that are loaded into memory, but haven't been used recently and may not even be needed at all. These are potential candidates for swapping. This memory would probably need to be flushed. Cached pages that have been some how cached that are likely to be easily reused. Cached memory probably would not require flushing. It is still possible for cached pages to be reactivated Free pages that are completely free and ready to be used.

值得注意的是,仅仅因为Mac OS X可能会显示很少的实际空闲内存,这可能不是一个很好的指示有多少准备在短时间内使用。

内存当前被我的进程使用

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

在Linux上,你不能/不应该通过SysInfo的freeram或在totalram上做一些算术来获得“总的可用物理内存”。

推荐的方法是读取proc/meminfo,引用kernel/git/torvalds/linux。Git, /proc/meminfo提供估计可用内存:

许多负载平衡和工作负载放置程序检查/proc/meminfo到 估计有多少可用的空闲内存。他们通常这样做 把"免费"和"缓存"加起来,十年前还好,但现在 今天肯定是错的。

在/proc/meminfo中提供这样的估计更方便。如果将来事情发生改变,我们只需要在一个地方改变它。

一种方法是亚当·罗森菲尔德(Adam Rosenfield)对“如何确定c++中Linux系统RAM的数量?”建议:读取文件,并使用fscanf抓取行(但不是去MemTotal,去MemAvailable)

同样,如果你想要得到使用的物理内存总量,这取决于你对“使用”的定义,你可能不想从totalram中减去freeram,而是从memtotal中减去memavailable来得到top或htop告诉你的东西。

在Windows中,你可以通过下面的代码获取CPU使用情况:

#include <windows.h>
#include <stdio.h>

//------------------------------------------------------------------------------------------------------------------
// Prototype(s)...
//------------------------------------------------------------------------------------------------------------------
CHAR cpuusage(void);

//-----------------------------------------------------
typedef BOOL ( __stdcall * pfnGetSystemTimes)( LPFILETIME lpIdleTime, LPFILETIME lpKernelTime, LPFILETIME lpUserTime );
static pfnGetSystemTimes s_pfnGetSystemTimes = NULL;

static HMODULE s_hKernel = NULL;
//-----------------------------------------------------
void GetSystemTimesAddress()
{
    if(s_hKernel == NULL)
    {
        s_hKernel = LoadLibrary(L"Kernel32.dll");
        if(s_hKernel != NULL)
        {
            s_pfnGetSystemTimes = (pfnGetSystemTimes)GetProcAddress(s_hKernel, "GetSystemTimes");
            if(s_pfnGetSystemTimes == NULL)
            {
                FreeLibrary(s_hKernel);
                s_hKernel = NULL;
            }
        }
    }
}
//----------------------------------------------------------------------------------------------------------------

//----------------------------------------------------------------------------------------------------------------
// cpuusage(void)
// ==============
// Return a CHAR value in the range 0 - 100 representing actual CPU usage in percent.
//----------------------------------------------------------------------------------------------------------------
CHAR cpuusage()
{
    FILETIME               ft_sys_idle;
    FILETIME               ft_sys_kernel;
    FILETIME               ft_sys_user;

    ULARGE_INTEGER         ul_sys_idle;
    ULARGE_INTEGER         ul_sys_kernel;
    ULARGE_INTEGER         ul_sys_user;

    static ULARGE_INTEGER     ul_sys_idle_old;
    static ULARGE_INTEGER  ul_sys_kernel_old;
    static ULARGE_INTEGER  ul_sys_user_old;

    CHAR usage = 0;

    // We cannot directly use GetSystemTimes in the C language
    /* Add this line :: pfnGetSystemTimes */
    s_pfnGetSystemTimes(&ft_sys_idle,    /* System idle time */
        &ft_sys_kernel,  /* system kernel time */
        &ft_sys_user);   /* System user time */

    CopyMemory(&ul_sys_idle  , &ft_sys_idle  , sizeof(FILETIME)); // Could been optimized away...
    CopyMemory(&ul_sys_kernel, &ft_sys_kernel, sizeof(FILETIME)); // Could been optimized away...
    CopyMemory(&ul_sys_user  , &ft_sys_user  , sizeof(FILETIME)); // Could been optimized away...

    usage  =
        (
        (
        (
        (
        (ul_sys_kernel.QuadPart - ul_sys_kernel_old.QuadPart)+
        (ul_sys_user.QuadPart   - ul_sys_user_old.QuadPart)
        )
        -
        (ul_sys_idle.QuadPart-ul_sys_idle_old.QuadPart)
        )
        *
        (100)
        )
        /
        (
        (ul_sys_kernel.QuadPart - ul_sys_kernel_old.QuadPart)+
        (ul_sys_user.QuadPart   - ul_sys_user_old.QuadPart)
        )
        );

    ul_sys_idle_old.QuadPart   = ul_sys_idle.QuadPart;
    ul_sys_user_old.QuadPart   = ul_sys_user.QuadPart;
    ul_sys_kernel_old.QuadPart = ul_sys_kernel.QuadPart;

    return usage;
}


//------------------------------------------------------------------------------------------------------------------
// Entry point
//------------------------------------------------------------------------------------------------------------------
int main(void)
{
    int n;
    GetSystemTimesAddress();
    for(n=0; n<20; n++)
    {
        printf("CPU Usage: %3d%%\r", cpuusage());
        Sleep(2000);
    }
    printf("\n");
    return 0;
}

Linux

读取内存和加载数的一种可移植方法是sysinfo调用

使用

   #include <sys/sysinfo.h>

   int sysinfo(struct sysinfo *info);

描述

   Until Linux 2.3.16, sysinfo() used to return information in the
   following structure:

       struct sysinfo {
           long uptime;             /* Seconds since boot */
           unsigned long loads[3];  /* 1, 5, and 15 minute load averages */
           unsigned long totalram;  /* Total usable main memory size */
           unsigned long freeram;   /* Available memory size */
           unsigned long sharedram; /* Amount of shared memory */
           unsigned long bufferram; /* Memory used by buffers */
           unsigned long totalswap; /* Total swap space size */
           unsigned long freeswap;  /* swap space still available */
           unsigned short procs;    /* Number of current processes */
           char _f[22];             /* Pads structure to 64 bytes */
       };

   and the sizes were given in bytes.

   Since Linux 2.3.23 (i386), 2.3.48 (all architectures) the structure
   is:

       struct sysinfo {
           long uptime;             /* Seconds since boot */
           unsigned long loads[3];  /* 1, 5, and 15 minute load averages */
           unsigned long totalram;  /* Total usable main memory size */
           unsigned long freeram;   /* Available memory size */
           unsigned long sharedram; /* Amount of shared memory */
           unsigned long bufferram; /* Memory used by buffers */
           unsigned long totalswap; /* Total swap space size */
           unsigned long freeswap;  /* swap space still available */
           unsigned short procs;    /* Number of current processes */
           unsigned long totalhigh; /* Total high memory size */
           unsigned long freehigh;  /* Available high memory size */
           unsigned int mem_unit;   /* Memory unit size in bytes */
           char _f[20-2*sizeof(long)-sizeof(int)]; /* Padding to 64 bytes */
       };

   and the sizes are given as multiples of mem_unit bytes.

窗户

上面的一些值可以很容易地从适当的Win32 API中获得,为了完整起见,我只是在这里列出它们。但是,其他的需要从Performance Data Helper库(PDH)中获得,这有点“不直观”,并且需要大量痛苦的试验和错误才能开始工作。(至少我花了不少时间,也许我只是有点笨…)

注意:为了清晰起见,以下代码省略了所有错误检查。检查返回码…!

Total Virtual Memory: #include "windows.h" MEMORYSTATUSEX memInfo; memInfo.dwLength = sizeof(MEMORYSTATUSEX); GlobalMemoryStatusEx(&memInfo); DWORDLONG totalVirtualMem = memInfo.ullTotalPageFile; 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. Virtual Memory currently used: 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; 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 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 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

在Linux上,一开始似乎显而易见的选择是使用getrusage()等POSIX api。我花了一些时间试图让它工作,但从未得到有意义的值。当我最终检查内核源代码本身时,我发现显然这些api在Linux内核2.6中还没有完全实现!?

最后,通过读取伪文件系统/进程和内核调用,我得到了所有的值。

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] = '\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; } 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: 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 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 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()和类似的函数代替?