当前位置:首页 > 文章列表 > 文章 > linux > Linux /proc 读取进程内存指标的方法

Linux /proc 读取进程内存指标的方法

来源:17golang原创 2026-10-17 08:36:55 0浏览 收藏

在 Linux 上读取进程内存,最实用的分层方式是:高频监控先读 /proc/PID/status,需要定位到具体内存映射时再读 /proc/PID/smaps。前者适合拿到 VmRSS、RssAnon、RssFile、RssShmem 和 VmSwap 等汇总指标,后者能把每个映射的 Rss、Pss、Private_Dirty、Swap 等信息展开,但读取成本更高。

把 status 当作低成本趋势数据,把 smaps 当作按需诊断数据;不要因为一次采集读不到文件,就把进程内存写成 0。
  • 进程级趋势:优先解析 /proc/PID/status。
  • 共享内存归因:关注 smaps 中的 Pss,而不是只看 Rss。
  • 高频采集:固定间隔读取 status,限制单次扫描字段。
  • 异常诊断:按需读取 smaps,并记录耗时、权限和 PID 是否仍存在。
Linux proc status 与 smaps 的内存指标关系说明图
图1:status 提供进程级汇总,smaps 展开到映射级内存指标。

先用 /proc/PID/status 做进程级汇总

/proc/PID/status 是面向人和程序都比较容易读取的文本快照。采集器通常不需要保存全部字段,可以只取内存相关键:VmSize 表示虚拟地址空间规模,VmRSS 表示驻留集近似值,RssAnon、RssFile、RssShmem 用来拆分驻留内存来源,VmSwap 则提示换出规模。

下面的 Python 片段只解析字段名和带单位的数值。注释说明了单位转换与未知字段处理;它没有把不存在的字段当成零,而是保留字段缺失状态。

from pathlib import Path

def read_status_memory(pid: int) -> dict[str, int]:
    # status 是文本快照;单位通常为 kB,统一换算成字节便于比较。
    path = Path(f"/proc/{pid}/status")
    wanted = {"VmSize", "VmRSS", "RssAnon", "RssFile", "RssShmem", "VmSwap"}
    result: dict[str, int] = {}
    try:
        for line in path.read_text().splitlines():
            key, sep, value = line.partition(":")
            if not sep or key not in wanted:
                # 忽略非目标字段,也兼容不同内核暴露的字段集合。
                continue
            parts = value.split()
            if not parts:
                continue
            # status 的内存数值带 kB;没有单位时不擅自换算。
            result[key] = int(parts[0]) * 1024 if len(parts) > 1 and parts[1] == "kB" else int(parts[0])
    except FileNotFoundError:
        # PID 可能在打开文件前退出;调用方应记录 missing,而不是写入内存为零。
        return {}
    except PermissionError:
        # 权限受限时保留空结果,让上层区分 permission_denied 与真实零值。
        return {}
    return result

这里的空字典只表示本次读取没有得到指标,生产采集器应同时返回状态码或错误类型。否则进程刚退出、容器 PID 命名空间不一致、权限被收紧,都会被误判为“内存突然降为 0”。

需要映射归因时读取 /proc/PID/smaps

smaps 按每个虚拟内存区域展开统计。它适合回答“这部分驻留内存属于哪类映射”“共享页应该如何分摊”这类 status 无法回答的问题。Rss 是映射当前驻留的页数,Pss 会按共享关系分摊;Private_Dirty、Shared_Dirty 和 Swap 则帮助区分私有脏页、共享脏页和换出页。

smaps 的信息量来自逐映射统计,因此不应像 status 一样对所有进程、所有周期无条件读取。下面的解析器只保留映射头和少数字段,遇到权限或进程消失时返回明确状态。

from pathlib import Path

def read_smaps(pid: int) -> tuple[str, list[dict[str, int | str]]]:
    # 每个地址区间开启一个记录,后续键值行归入该映射。
    current: dict[str, int | str] | None = None
    mappings: list[dict[str, int | str]] = []
    try:
        for line in Path(f"/proc/{pid}/smaps").read_text().splitlines():
            if "-" in line and line.split(" ", 1)[0].count("-") == 1:
                # 映射头只保存地址范围,避免把路径和权限当成指标。
                current = {"range": line.split()[0]}
                mappings.append(current)
                continue
            key, sep, value = line.partition(":")
            if current is None or not sep or key not in {"Rss", "Pss", "Private_Dirty", "Swap"}:
                continue
            parts = value.split()
            if parts:
                # 这些字段通常以 kB 表示,统一转成字节;缺失字段不补零。
                current[key] = int(parts[0]) * 1024 if len(parts) > 1 and parts[1] == "kB" else int(parts[0])
    except FileNotFoundError:
        return "missing", []
    except PermissionError:
        return "permission_denied", []
    return "ok", mappings

这段代码只展示解析边界,不代表需要把全部映射长期存储。实际系统通常只保留聚合结果,例如 Pss 总和、Private_Dirty 最大的若干映射,以及本次读取耗时。

把读取成本分成快照层和诊断层

一套可扩展的采集器可以分成两层:status 是快照层,按固定周期读取少数字段;smaps 是诊断层,只有进程超过内存阈值、趋势异常或用户主动排障时才读取。这样既能保持时间序列的连续性,也能避免每个采样周期都扫描大量映射。

规模化部署时还要记录三个运行信号:读取耗时、返回状态和采集字段数。smaps 读取慢不一定代表进程异常,也可能只是映射数量多;PID 消失不应参与内存聚合;权限拒绝则应进入采集器自身的错误指标。

Linux 进程内存采集的快照层与诊断层结构说明图
图2:采集器把 status 快照层与 smaps 诊断层分开,按成本选择数据粒度。

如果只想监控 RSS 趋势,status 已经足够;如果要解释共享库、匿名映射或脏页来源,才值得接受 smaps 的额外开销。不要把两者的数字直接相加:status 是进程级汇总,smaps 是映射级明细,统计口径不同。

PID 消失、权限和字段变化怎么处理

读取 /proc 不是对稳定文件的普通读取。进程可能在打开前退出,命名空间中的 PID 可能与宿主机不同,权限策略也可能让同一个路径在不同运行身份下不可见。采集器至少应区分 ok、missing、permission_denied 和 parse_error。

字段方面,应按键名选择性解析并允许未知字段存在。内核版本、配置和进程状态会影响可见字段;稳定做法是把缺失字段表示为“未提供”,只对确实读到的指标做聚合。若必须比较多台机器,先统一单位、采样周期和 PID 命名空间,再比较趋势。

常见问题

VmRSS 和 smaps 的 Rss 一定相等吗?

它们都描述驻留内存,但一个是进程级汇总、一个是映射级明细,读取时刻和统计口径可能不同,不应把瞬时差异直接当作故障。

为什么不每秒读取一次 smaps?

smaps 需要逐映射提供更多统计,映射多的进程读取成本更高。高频趋势使用 status,smaps 留给异常诊断或低频采样更稳妥。

读取失败时可以把指标写成 0 吗?

不可以。0 是真实指标,读取失败是采集状态;混用会制造假的内存下降,应保留错误类型并让上层决定是否插值。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Linux 用 systemd timer 替代定时任务的迁移Linux 用 systemd timer 替代定时任务的迁移
上一篇
Linux 用 systemd timer 替代定时任务的迁移
HTMLInputElement.showPicker 怎样用自定义按钮打开选择器
下一篇
HTMLInputElement.showPicker 怎样用自定义按钮打开选择器
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    500次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    558次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    587次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    539次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    349次使用