You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

用C程序计算进程总内存使用量结果偏大的问题排查咨询

进程内存统计的问题分析与解决办法

为什么你的计算结果偏大?

你总结的两个原因完全命中了核心:

  1. 内存页对齐的影响:/proc/PID/maps里的地址范围是按内核页大小(你的是4KB)对齐的,每个段的大小都是页的整数倍,但进程实际用到的可能只是页内部分空间。直接计算地址差会把整个页都算进去,得到的是虚拟内存的总量,而非实际物理内存使用量。
  2. 未过滤已删除的内存段:标记为(deleted)的段大多是文件被删除但进程还持有的映射,其中不少已经不再被访问,内核可能已经回收了对应的物理内存,但你的代码依然把它们全部计入总量,自然会虚高。

除此之外,你的代码还有几个容易忽略的问题:

  • 错误处理不完整:当space或hyphen为NULL时,代码没有终止当前循环,会触发未定义行为
  • 没有区分共享与私有内存:带s权限位的共享段是多个进程共用的,如果你要统计进程的独占内存,不应该把这些共享段全部算进去,否则会重复统计共享内存的总量

修正统计逻辑的几个方向

1. 优化/proc/PID/maps的处理

如果坚持用maps,需要做这些过滤:

  • 跳过标记为(deleted)的段(可通过检查行尾的(deleted)字符串判断)
  • 区分权限位:只统计带p(私有)权限的段,或者对共享段做特殊处理(比如只计算进程实际修改过的私有页,但这点maps里看不到,得用smaps)
  • 跳过内核特殊映射:比如[vdso]、[vsyscall]、[vvar]这类内核提供的虚拟映射,不属于进程的实际内存开销

2. 用/proc/PID/smaps获取更精准的统计

/proc/PID/smaps是比maps更详细的内存映射统计文件,每个段都包含了Rss(实际驻留物理内存)、Pss(比例共享内存,把共享部分按使用进程数拆分)、PrivateDirty(进程独占的脏页)等关键字段,是目前统计进程内存最靠谱的数据源之一。

比如你可以累加所有段的Pss值,得到进程实际“消耗”的内存(既包含独占部分,也合理分摊了共享部分)。下面是一个简单的实现示例:

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

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "用法: %s <进程PID>\n", argv[0]);
        return 1;
    }

    char path[256];
    snprintf(path, sizeof(path), "/proc/%s/smaps", argv[1]);
    FILE *fd = fopen(path, "r");
    if (!fd) {
        perror("打开smaps文件失败");
        return 1;
    }

    char *line = NULL;
    size_t len = 0;
    unsigned long total_pss = 0;
    unsigned long total_rss = 0;

    while (getline(&line, &len, fd) != -1) {
        // 提取PSS值(单位KB,转成字节)
        if (strncmp(line, "Pss:", 4) == 0) {
            unsigned long pss;
            sscanf(line + 4, "%lu", &pss);
            total_pss += pss * 1024;
        }
        // 提取RSS值(实际物理驻留内存)
        if (strncmp(line, "Rss:", 4) == 0) {
            unsigned long rss;
            sscanf(line + 4, "%lu", &rss);
            total_rss += rss * 1024;
        }
    }

    printf("总PSS内存: %lu 字节\n", total_pss);
    printf("总RSS内存: %lu 字节\n", total_rss);

    free(line);
    fclose(fd);
    return 0;
}

3. 解析/proc/PID/status获取内核预统计值

如果你不想自己拆分内存段,可以直接读取/proc/PID/status文件,里面的字段都是内核已经统计好的:

  • VmRSS:进程实际驻留的物理内存大小(包含共享部分)
  • VmSize:进程的虚拟内存总大小
  • VmData:数据段(堆)的大小
  • VmStk:栈的大小
  • VmExe:可执行代码段的大小

比如读取VmRSS的逻辑很简单,只需要在文件中找到以VmRSS:开头的行,提取对应的数值即可。

关键注意事项

内存统计没有绝对的“最准确”,取决于你的需求:

  • 如果要统计进程实际占用的物理内存,PSS是最合理的指标,它避免了共享内存的重复统计
  • 如果要统计进程的虚拟内存空间总量,VmSize或过滤后的maps计算结果更合适
  • 对于(deleted)的段,不要直接全部剔除,有些可能还在被进程使用,最好结合smaps里的Rss值判断,如果Rss为0再忽略

内容的提问来源于stack exchange,提问作者Mathew

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 22:26:01