C语言中当前进程的内存使用情况

31

我需要在C中获取当前进程的内存使用情况。有人可以提供在Linux平台上如何实现此操作的代码示例吗?

我知道可以通过cat /proc/<your pid>/status方法获取内存使用情况,但我不知道如何在C中捕获它。

顺便说一句,这是我修改的PHP扩展(尽管我是C新手)。如果在PHP扩展API中有可用的快捷方式,那就更有帮助了。

7个回答

32

getrusage库函数返回一个结构体,其中包含有关当前进程的大量数据,包括以下内容:

long   ru_ixrss;         /* integral shared memory size */
long   ru_idrss;         /* integral unshared data size */
long   ru_isrss;         /* integral unshared stack size */

然而,最新的Linux文档对这三个字段有以下说法:

(unmaintained) This field is currently unused on Linux

手册定义了它为:

并非所有字段都被填写;未维护的字段由内核设置为零。(未维护的字段是为了与其他系统兼容,并因为它们可能有一天会在Linux上得到支持。)

请参阅getrusage(2)


2
很遗憾,我的内核(Ubuntu Hardy)中没有ru_idrss和ru_isrss数据:http://linux.die.net/man/2/getrusage - scotts
很不幸,我的内核(Debian Wheezy)上所有数据都显示为0。 - Achim

30

您始终可以像打开常规文件一样打开/proc系统中的“文件”(使用“self”符号链接,以便无需查找自己的pid):

FILE* status = fopen( "/proc/self/status", "r" );
当然,现在你需要解析文件以提取所需的信息。

1
有人可能会尝试解析/proc/self/stat,其中只包含数字而没有标签,并且根据man proc的说法被ps使用。或者是statm,它是内存的stat子集:https://dev59.com/QHI_5IYBdhLWcg3wBuX3#7212248 Matthieu提到这些对于大页面可能是错误的:https://dev59.com/QHI_5IYBdhLWcg3wBuX3#7212248?noredirect=1#comment101785466_7212248 我必须测试一下。 - Ciro Santilli OurBigBook.com
我使用 system("cat /proc/self/status | egrep 'VmSize|VmRSS'"); 来获取,但与fopen读取的结果不同。不知道为什么。 - undefined
1
@LewisChan 因为你的 /proc/self 是指向 cat 的。使用 egrep '...' /proc/self/status 来获取 egrep 的内存使用情况。:) 更严肃地说,如果你想要派生出一个辅助进程,你必须用你的PID替换self - undefined

18

这是一种非常丑陋和不可移植的方式来获取内存使用情况,但由于在Linux上 getrusage() 的内存跟踪基本上没用,读取 /proc/<pid>/statm 是我所知道的在Linux上获取信息的唯一方法。

如果有人知道更干净、更跨Unix的跟踪内存使用情况的方法,我会非常感兴趣学习。

typedef struct {
    unsigned long size,resident,share,text,lib,data,dt;
} statm_t;

void read_off_memory_status(statm_t& result)
{
  unsigned long dummy;
  const char* statm_path = "/proc/self/statm";

  FILE *f = fopen(statm_path,"r");
  if(!f){
    perror(statm_path);
    abort();
  }
  if(7 != fscanf(f,"%ld %ld %ld %ld %ld %ld %ld",
    &result.size,&result.resident,&result.share,&result.text,&result.lib,&result.data,&result.dt))
  {
    perror(statm_path);
    abort();
  }
  fclose(f);
}

来自 proc(5) 页的手册:

   /proc/[pid]/statm
          Provides information about memory usage, measured in pages.  
          The columns are:

              size       total program size
                         (same as VmSize in /proc/[pid]/status)
              resident   resident set size
                         (same as VmRSS in /proc/[pid]/status)
              share      shared pages (from shared mappings)
              text       text (code)
              lib        library (unused in Linux 2.6)
              data       data + stack
              dt         dirty pages (unused in Linux 2.6)

3
这种方法的问题在于“以页面为单位”的部分。如果您的进程使用了三个4 kB页面、两个2 MB页面和一个1 GB页面,它会报告使用了6个页面。虽然在技术上是正确的,但对于推断以字节为单位的RSS毫无用处。 - Matthieu M.
@MatthieuM。man proc 还指出 "/proc/[pid]/status 提供了 /proc/[pid]/stat/proc/[pid]/statm 中的大部分信息",并且 ps 使用 stat,这意味着 /proc/self/statusps 本身也存在这个问题,你是否理解?你能否引用一些显示单个进程可以具有多个不同大小页面的内容?谢谢! - Ciro Santilli OurBigBook.com
1
@CiroSantilli新疆改造中心996ICU六四事件:我只能引用我自己使用手动大页面+常规页面的经验。 - Matthieu M.
@MatthieuM,谢谢,我不知道mmap``MAP_HUGE* :-) 这很令人难过。 - Ciro Santilli OurBigBook.com
你能在C语言中使用&吗?我以为那是C++的特性,不是吗?我的意思是,该函数应该接受一个statm_t *result指针。我有什么遗漏吗? - SRG
&x 是原始的 C 语言,表示“指向变量 x 的地址”。在 C++ 中,有点令人困惑的是,同样的字符用于类型声明,表示引用。 - James

9

2
不幸的是,在我的内核(Debian Wheezy)上,ru_maxrss始终显示为0。 - Achim
2
这在Linux 4.12.4上似乎可以工作。虽然我想知道它是否实际返回千字节。 - avl_sweden

8

虽然我来晚了,但是对于其他寻找Linux中驻留内存和虚拟内存(以及它们目前的峰值)的人,这可能很有帮助。

虽然这种方法可能非常糟糕,但它可以完成任务。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


/*
 * Measures the current (and peak) resident and virtual memories
 * usage of your linux C process, in kB
 */
void getMemory(
    int* currRealMem, int* peakRealMem,
    int* currVirtMem, int* peakVirtMem) {

    // stores each word in status file
    char buffer[1024] = "";

    // linux file contains this-process info
    FILE* file = fopen("/proc/self/status", "r");

    // read the entire file
    while (fscanf(file, " %1023s", buffer) == 1) {

        if (strcmp(buffer, "VmRSS:") == 0) {
            fscanf(file, " %d", currRealMem);
        }
        if (strcmp(buffer, "VmHWM:") == 0) {
            fscanf(file, " %d", peakRealMem);
        }
        if (strcmp(buffer, "VmSize:") == 0) {
            fscanf(file, " %d", currVirtMem);
        }
        if (strcmp(buffer, "VmPeak:") == 0) {
            fscanf(file, " %d", peakVirtMem);
        }
    }
    fclose(file);
}

注意:当file不存在时,我无法处理。 - Anti Earth
"不应该把int *改成int,并且将fscanf参数改为例如&currRealMem而不是currRealMem吗???" - mwag
@mwag 不,这个函数通过修改引用的基元来“返回”。您需要将调用者基元的引用传递给此函数。例如:int a、b、c、d; getMemory(&a, &b, &c, &d); - Anti Earth
我的错,我把那些int*当成了局部变量而不是函数参数。 - mwag
请注意,/proc/self/status的fopen有时会失败,请进行检查。 - GroovyDotCom

8
#include <sys/resource.h>
#include <errno.h>

errno = 0;
struct rusage memory;
getrusage(RUSAGE_SELF, &memory);
if(errno == EFAULT)
    printf("Error: EFAULT\n");
else if(errno == EINVAL)
    printf("Error: EINVAL\n");
printf("Usage: %ld\n", memory.ru_ixrss);
printf("Usage: %ld\n", memory.ru_isrss);
printf("Usage: %ld\n", memory.ru_idrss);
printf("Max: %ld\n", memory.ru_maxrss);

我使用了这段代码,但无论何时都会得到4个printf()输出0的结果。

10
这是因为,即使在 POSIX.1 发布十年后的 Linux 2.6 版本中,仍然只有少数字段实现了 getrusage()。据称,获取信息的唯一方法是通过内核调用或读取 /proc/<pid>/statm(参见手册页 man 5 proc),这完全不可移植。 :-( - James
7
为什么你在一个静态大小的结构体中使用malloc? - Not a Name

0

上述结构体取自4.3BSD Reno。在Linux下,并非所有字段都有意义。在Linux 2.4中,仅维护ru_utime、ru_stime、ru_minflt和ru_majflt字段。自Linux 2.6以来,还维护ru_nvcsw和ru_nivcsw字段。

http://www.atarininja.org/index.py/tags/code


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接