Linux procfs 怎样查看进程内存映射
来源:17golang原创
时间:2026-11-12 10:35:13 441浏览 收藏
我排查进程内存占用时,最先想看的通常不是调试器,而是 Linux 自带的 procfs。对一个已知 PID,/proc/PID/maps 能告诉我们进程当前有哪些虚拟地址区域、每个区域具有什么访问权限、对应哪个文件偏移,以及是否关联了文件路径。要看每个区域的 RSS、PSS 等更细的统计,再切换到 /proc/PID/smaps。
官方文档地址:https://docs.kernel.org/filesystems/proc.html
这篇只解决“怎样查看进程内存映射”这一件事:先读懂 maps 的一行记录,再处理 PID 和权限,最后判断什么时候需要 smaps。它不涉及修改进程内存、内存转储或调试器命令。
先看 /proc/PID/maps 返回什么
最小读取方式就是把 PID 放进路径:
pid="1234" # 读取目标进程当前的虚拟内存映射;输出每行代表一个区域。 cat "/proc/$pid/maps"
官方文档给出的记录形态可以概括为:
address perms offset dev inode pathname 7f2c10000000-7f2c10021000 r--p 00000000 08:01 12345 /usr/lib/libexample.so 7f2c10021000-7f2c1008a000 r-xp 00021000 08:01 12345 /usr/lib/libexample.so 55a00000-55a21000 rw-p 00000000 00:00 0 [heap]
从左到右看,一行里最重要的是六组信息:
- address:左侧是起始虚拟地址,右侧是结束地址,区间通常按半开区间理解。
- perms:四个字符分别表达读、写、执行,以及共享
s或私有写时复制p。 - offset:如果区域来自文件,它表示映射在该文件中的偏移。
- dev 与 inode:帮助把映射关联到设备和文件对象;匿名区域常见 inode 为
0。 - pathname:可能是库文件,也可能是
[heap]、[stack]、[vdso]等内核提供的特殊标记,或者为空。

我第一次看这类输出时容易只盯着路径。实际上,r-xp 和 rw-p 往往代表同一个文件的不同映射区域;要判断某个区域能否写入或执行,应该先看权限列,再看路径列。
用 self 和指定 PID 读取映射
如果只想查看当前命令本身对应的进程,可以用 /proc/self/maps,这样不需要先手动找 PID:
# self 会解析为“正在读取这个路径的进程”。
cat /proc/self/maps
# 只保留带文件路径的映射,便于先观察可执行文件和共享库。
awk 'NF >= 6 { print }' /proc/self/maps
查看其他进程时,把已知 PID 放入路径即可:
pid="1234" # 先确认目标目录存在,避免把进程已退出误判成权限问题。 if [ -d "/proc/$pid" ]; then # 读取映射;失败时保留内核返回的权限错误。 cat "/proc/$pid/maps" else # PID 可能已经退出,/proc 下的目录也会随之消失。 printf '进程不存在:%s\n' "$pid" >&2 exit 1 fi
读取自己的 /proc/PID/* 通常不需要额外权限;读取其他进程的只读信息时,内核文档提到需要满足相应的 CAP_SYS_PTRACE 与 PTRACE_MODE_READ 检查,某些环境还可由 CAP_PERFMON 满足。容器、不同用户和受限安全策略都可能让同一条命令在不同环境中得到 Permission denied。
按权限和特殊路径定位区域
拿到输出后,可以先按权限分组,而不是把每个地址都当成一个独立故障。下面的命令只做文本筛选,不改变进程:
pid="1234" # 显示可执行区域,常用于定位程序正文、共享库代码段或 JIT 相关区域。 grep -E ' [r-][w-]x[sp] ' "/proc/$pid/maps" # 显示堆和栈的特殊映射,帮助区分常规文件映射与进程运行时区域。 grep -E '\[heap\]|\[stack' "/proc/$pid/maps"
常见标记可以这样理解:
| 标记或权限 | 通常代表 | 排查时看什么 |
|---|---|---|
r-xp | 私有、可读、可执行区域 | 程序或共享库的代码映射,结合 pathname 和 offset 判断来源 |
rw-p | 私有、可读写区域 | 数据段、堆或私有写时复制区域 |
[heap] | 进程堆 | 观察地址范围,不把它当成磁盘文件 |
[stack] | 主线程栈 | 区分主线程栈与其他线程的栈映射 |
[vdso] | 内核为用户态提供的虚拟动态对象 | 它没有普通共享库路径,不能按磁盘文件处理 |
这里的“通常”很重要:不同架构、内核配置、进程运行方式和映射来源都会影响实际输出。maps 适合先回答“哪些区域存在、权限是什么、来自哪里”,不适合单独回答“每个区域当前占用了多少物理内存”。
需要内存统计时切换到 smaps
当问题从“有哪些映射”变成“哪个映射消耗了多少内存”,我会改读 smaps:
pid="1234" # smaps 在 maps 的每个区域后面增加 Size、Rss、Pss 等统计字段。 cat "/proc/$pid/smaps" # 只提取区域首行和 RSS/PSS,减少排查时的视觉噪声。 awk '/^[0-9a-f]+-[0-9a-f]+ / || /^Rss:|^Pss:/' "/proc/$pid/smaps"
smaps 是基于 maps 的扩展。一个区域的首行仍然包含地址、权限、偏移、设备号、inode 和路径,后面会跟着 Size、Rss、Pss、共享/私有脏页等字段。简单选择可以记成:
- 只想知道区域范围、读写执行权限和文件来源:看
maps。 - 想知道区域有多少页驻留、共享成本如何分摊:看
smaps。 - 只想要全进程汇总:可以关注
smaps_rollup,但它仍然属于统计读取,成本和权限要单独评估。

读取 smaps 比读取 maps 更重,脚本如果只需要判断区域来源,就没有必要默认扫描全部统计字段。先用 maps 缩小范围,再对少量目标或必要时刻读取 smaps,通常更容易控制开销。
处理读取时的权限与一致性边界
这一步是我后来补上的经验:能打开文件,不代表拿到的是不会变化的“内存快照”。官方文档明确指出,读取 maps 或 smaps 本身可能与进程修改地址空间同时发生竞态;完整读取可以获得内核定义的保证,但分段读取时不能假设所有行来自同一时刻。
因此排查脚本至少要记录三件事:
- 读取开始时的 PID,以及目标是否在读取期间退出。
- 读取的是
maps还是smaps,不能把二者的字段混为一谈。 - 当前用户、容器和能力边界;对其他 PID 失败时,先区分权限拒绝和进程消失。
pid="1234"
# 把权限失败、进程退出和成功读取区分开,便于日志记录。
if [ ! -r "/proc/$pid/maps" ]; then
printf '无法读取 maps:请检查 PID 是否存在及当前权限\n' >&2
exit 2
fi
# 单次读取后再处理文本,避免脚本多次打开文件扩大观察窗口。
maps_data=$(cat "/proc/$pid/maps") || {
printf '读取过程中进程可能已退出或映射发生变化\n' >&2
exit 3
}
printf '%s\n' "$maps_data"
这段脚本不能把 procfs 变成严格的事务快照,但能让失败原因更接近真实情况。特别是长时间运行的进程,PID 可能在读取前后被回收再分配;要做持续监控时,最好同时记录进程启动标识或其他身份信息,不要只把 PID 当作永久身份。
整理一份可复用的排查清单
- 先确认 PID 仍然存在;只看当前进程时优先用
/proc/self/maps。 - 读取
maps,把地址区间、权限、offset、dev、inode 和 pathname 分开看。 - 遇到
[heap]、[stack]、[vdso]或空路径时,不要按普通库文件推断。 - 需要 RSS、PSS 或脏页信息时,再读取
smaps或评估smaps_rollup。 - 读取其他 PID 失败时,同时排查权限、容器边界和进程在读取期间退出。
- 对结果做时间序列比较时,记录读取时间、PID 身份和读取文件类型,避免把竞态差异当成确定的内存变化。
对我来说,procfs 的价值在于它把“进程内存”拆成了可以直接阅读的区域记录:maps 负责告诉你区域在哪里、能做什么、来自哪里;smaps 再补充这些区域的内存统计。先选对文件,再解释权限和一致性边界,排查结果会比只看一个总内存数字可靠得多。
相关问题
为什么 /proc/PID/maps 里有些行没有 pathname?
这通常表示映射没有关联到普通文件,或者属于匿名映射。可以结合权限、offset、inode 和相邻区域判断,不要把空路径当成读取失败。
maps 和 smaps 可以互相替代吗?
不能。maps 描述地址区域和权限,smaps 在此基础上提供每个映射的内存统计,读取成本和输出规模也更大。
读取其他进程的 maps 被拒绝怎么办?
先确认 PID 没有退出,再检查当前用户、容器边界和内核能力。不要为了读取一个普通排障字段就无条件扩大权限,先明确任务是否真的需要目标进程的映射信息。
-
426 收藏
-
387 收藏
-
242 收藏
-
238 收藏
-
402 收藏
-
348 收藏
-
157 收藏
-
230 收藏
-
177 收藏
-
405 收藏
-
106 收藏
-
159 收藏
-
254 收藏
-
377 收藏
-
275 收藏
-
245 收藏
-
500 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习