Linux命令-pmap(进程内存映射报告工具)

📋 快速参考pmap 用于 报告进程的内存映射关系,是 Linux 系统调试与运维的重要工具。它可以清晰展示进程占用的 虚拟内存区域(VMA),包括每个内存段的起始地址、大小、权限以及对应的文件映射。


📌 目录


🔰 命令简介

pmap(Process Map)是 Linux 下用于 查看进程虚拟地址空间分布 的命令行工具。它通过读取 /proc/<pid>/maps/proc/<pid>/smaps 文件来获取进程的内存映射信息。

💡 核心能力

能力 说明
内存布局分析 查看进程的代码段、数据段、堆、栈及共享库的映射位置
内存使用统计 统计各内存区域的大小、驻留集大小(RSS)及脏页数量
匿名内存识别 区分文件映射内存与匿名映射内存(标记为 [ anon ]
权限检查 每段内存的读写执行权限一目了然(r/w/x)

💡 适用场景

场景 说明
内存泄漏排查 观察进程内存持续增长的区域,定位泄漏来源
性能调优 分析共享库加载情况,优化内存占用
安全审计 检查是否有可写且可执行的内存段(rwx
故障诊断 进程 OOM 或被 kill 时分析内存使用状况

⚠️ 本质原理pmap 是对 /proc/<pid>/maps/proc/<pid>/smaps 文件的格式化展示工具。也可以直接阅读这些文件获得相同信息。


📖 语法格式

bash 复制代码
pmap [选项] <PID> [...]

参数说明

参数 说明
PID 必填 目标进程的进程 ID,可同时指定多个
选项 可选 控制输出格式和详细程度的参数

基本用法

bash 复制代码
# 查看 PID 为 1234 的进程的内存映射
pmap 1234

# 同时查看多个进程
pmap 1234 5678

# 查看当前 Shell 进程
pmap $$

⚙️ 常用选项

选项 说明
-x, --extended 显示扩展格式,包含地址、大小、RSS、脏页、权限、映射文件
-d, --device 显示设备号(主设备号:次设备号)
-q, --quiet 安静模式,不显示头部和尾部汇总行
-A, --range LOW,HIGH 仅显示指定地址范围内的映射
-X -x 更详细,显示 RSS 和脏页差值信息
-XX 显示内核提供的所有信息(需要较新的内核支持)
-p, --show-path 显示映射文件的完整路径
-c, --read-rc 读取默认配置文件
-C, --read-rc-from FILE 从指定文件读取配置
-V, --version 显示版本信息
-h, --help 显示帮助信息

⚡ 各发行版注意事项

发行版 提供包 说明
RHEL/CentOS 7/8/9 procps-ng 默认安装,pmapprocps-ng 一起提供
Ubuntu/Debian procps 默认安装,无需额外安装
Alpine Linux procps 最小化安装可能不包含,需 apk add procps
openSUSE procps 默认安装
Arch Linux procps-ng 默认安装
bash 复制代码
# 检查 pmap 是否可用
which pmap && pmap --version

# Alpine 安装(如未预装)
apk add procps

📊 输出字段说明

默认输出格式

bash 复制代码
$ pmap 1
1:   /usr/lib/systemd/systemd
0000555555554000   1608K r-x-- systemd
00005555558e7000    140K r---- systemd
000055555590a000     52K rw--- systemd
0000555555c8d000    688K rw---   [ anon ]
00007ffff7a00000   1828K r-x-- libc-2.31.so
00007ffff7bc9000   2044K ----- libc-2.31.so
00007ffff7dc8000     16K r---- libc-2.31.so
00007ffff7dcc000      8K rw--- libc-2.31.so
00007ffff7dce000     52K rw---   [ anon ]
00007ffff7fcf000     12K rw---   [ anon ]
00007ffffffde000    132K rw---   [ stack ]
 total            32956K

扩展输出(-x)字段详解

bash 复制代码
$ pmap -x 1
Address           Kbytes     RSS   Dirty Mode  Mapping
0000555555554000    1608    1188       0 r-x-- systemd
00005555558e7000     140     140     140 r---- systemd
000055555590a000      52      52      52 rw--- systemd
0000555555c8d000     688     340     340 rw---   [ anon ]
...
total kB           32956    8800    7364
字段 说明
Address 虚拟内存区域的起始地址(十六进制)
Kbytes 该区域的总大小(KB),虚拟内存占用
RSS 常驻内存集大小(KB),实际占用的物理内存
Dirty 脏页大小(KB),被修改但尚未写回磁盘的页面
Mode 内存权限:r=读, w=写, x=执行, s=共享, p=私有
Mapping 映射来源:共享库路径 / [ anon ](匿名内存)/ [ stack ](栈)/ [ heap ](堆)

匿名内存标记说明

标记 含义
[ anon ] 匿名映射------没有对应文件的内存区域(如 malloc 分配的内存)
[ stack ] 进程的主线程栈
[ heap ] 进程的堆区域(brk() 系统调用扩展)
[ vvar ] 内核暴露给用户空间的只读变量页(vDSO 数据区)
[ vdso ] 虚拟动态共享对象,加速系统调用
[ vsyscall ] 旧版系统调用加速机制(已被 vDSO 取代)

🔧 实战示例

1. 查看指定进程的内存映射

bash 复制代码
# 获取 nginx 的主进程 PID
nginx_pid=$(pgrep -f "nginx: master" | head -1)

# 查看 nginx 主进程的内存布局
pmap $nginx_pid | head -30

2. 扩展格式查看详细信息

bash 复制代码
# -x 显示 RSS 和脏页
pmap -x 1

# -d 额外显示设备号
pmap -d 1

# 组合使用
pmap -x -d 1

3. 查看特定地址范围

bash 复制代码
# 仅查看 0x7f0000000000 到 0x7fffffffffff 范围内的映射(通常是共享库区域)
pmap -A 0x7f0000000000,0x7fffffffffff 1234

4. 统计进程的 RSS 总量

bash 复制代码
# 使用扩展模式,只看汇总行
pmap -x $$ | tail -1

# 输出示例:
# total kB          32956    8800    7364
# 解释:虚拟内存 32956KB,物理内存 8800KB,脏页 7364KB

5. 分析内存使用占比

bash 复制代码
# 查看各映射区域按大小排序
pmap -x $$ | sort -k3 -n -r | head -20

6. 批量检查多个相关进程

bash 复制代码
# 查看所有 nginx 进程的内存映射
pgrep nginx | xargs -I {} sh -c 'echo "=== PID: {} ==="; pmap -x {} | tail -1'

🔄 与其他工具对比

工具 定位 优势 劣势
pmap 进程内存映射报告 简洁直观,适合快速查看 无法实时更新
/proc/<pid>/maps 原始数据源 随时可读,无需安装工具 格式不友好,无汇总
/proc/<pid>/smaps 详细内存统计 包含 PSS、Swap 等字段 数据量大,需手动汇总
smem 内存使用统计 支持 PSS/USS,可按用户汇总 需要额外安装
ps 进程状态 显示总 RSS/VSZ 无法看到内存分布细节
top / htop 实时监控 动态更新,交互式 无法显示内存映射详情
valgrind massif 堆分析 详细的堆内存使用趋势 性能开销大,不适合生产环境

不同工具获取信息的对比

bash 复制代码
# pmap:直观的内存映射报告
pmap -x $$

# ps:总内存占用概览
ps -o pid,rss,vsz,comm -p $$

# /proc/PID/smaps:最详细的信息
cat /proc/$$/smaps | grep -E "^(Pss|Rss|Swap):" | awk '{sum+=$2} END {print "PSS:", sum, "kB"}'

# smem:PSS 统计(需安装)
smem -p -P $$ 2>/dev/null

🎯 高级用法

1. 监控进程内存增长(排查内存泄漏)

bash 复制代码
#!/bin/bash
# 每 5 秒记录一次进程的 RSS 变化
PID=$1
INTERVAL=5
COUNT=12

echo "时间,RSS(KB),Dirty(KB)"
for i in $(seq 1 $COUNT); do
    stats=$(pmap -x $PID 2>/dev/null | tail -1 | awk '{print $3, $4}')
    echo "$(date +%H:%M:%S),$stats"
    sleep $INTERVAL
done

2. 分析匿名内存占用

bash 复制代码
# 找出进程中所有匿名内存区域的总大小
pmap -x $$ | grep "\[ anon \]" | awk '{sum+=$2} END {print "匿名内存总计:", sum, "KB"}'

# 找出 RSS 最大的匿名区域
pmap -x $$ | grep "\[ anon \]" | sort -k3 -n -r | head -5

3. 检查可写且可执行的内存区域(安全检查)

bash 复制代码
# rwx 权限段意味着该内存区域既可写又可执行------存在安全隐患
pmap $$ | grep rwx

# 正常情况下应该没有或极少输出
# 大量 rwx 段可能表明使用了不安全的 JIT 编译或存在安全漏洞

4. 对比进程前后内存变化

bash 复制代码
# 记录基准状态
pmap -x $$ > /tmp/pmap_before.txt

# 执行某些操作...
# sleep 30

# 记录后续状态
pmap -x $$ > /tmp/pmap_after.txt

# 对比 RSS 总量变化
echo "Before: $(tail -1 /tmp/pmap_before.txt | awk '{print $3}') KB"
echo "After:  $(tail -1 /tmp/pmap_after.txt | awk '{print $3}') KB"

5. 生成进程内存分布报告

bash 复制代码
#!/bin/bash
# 将内存映射按类型分类汇总
PID=${1:-$$}

echo "=== PID: $PID 内存分类汇总 ==="
echo ""

pmap -x $PID 2>/dev/null | awk '
NR>2 && !/^total/ {
    if ($5 ~ /\[stack\]/)  { stack+=$2; rss_stack+=$3 }
    else if ($5 ~ /\[heap\]/) { heap+=$2; rss_heap+=$3 }
    else if ($5 ~ /\[anon\]/) { anon+=$2; rss_anon+=$3 }
    else if ($5 ~ /\[vdso\]|\[vvar\]/) { vdso+=$2; rss_vdso+=$3 }
    else if ($5 ~ /\.so/) { libs+=$2; rss_libs+=$3 }
    else if ($5 !~ /\[/) { code+=$2; rss_code+=$3 }
    else { other+=$2; rss_other+=$3 }
}
END {
    printf "%-20s %10s %10s\n", "类别", "虚拟内存(KB)", "物理内存(KB)"
    printf "%-20s %10s %10s\n", "----", "------------", "------------"
    printf "%-20s %10d %10d\n", "代码段(可执行文件)", code, rss_code
    printf "%-20s %10d %10d\n", "共享库(.so)", libs, rss_libs
    printf "%-20s %10d %10d\n", "堆(heap)", heap, rss_heap
    printf "%-20s %10d %10d\n", "栈(stack)", stack, rss_stack
    printf "%-20s %10d %10d\n", "匿名映射(anon)", anon, rss_anon
    printf "%-20s %10d %10d\n", "vDSO/vvar", vdso, rss_vdso
    printf "%-20s %10d %10d\n", "其他", other, rss_other
    printf "%-20s %10s %10s\n", "----", "------------", "------------"
    printf "%-20s %10d %10d\n", "总计", code+libs+heap+stack+anon+vdso+other, rss_code+rss_libs+rss_heap+rss_stack+rss_anon+rss_vdso+rss_other
}'

⚠️ 常见问题

1. 权限不足

bash 复制代码
# 查看 root 进程会报错
$ pmap 1
1:   Operation not permitted

# 解决方案:使用 sudo
sudo pmap 1

⚠️ 注意 :从内核 3.2+ 开始,普通用户只能查看自己拥有的进程的 /proc/<pid>/maps(受 ptrace 访问控制限制)。

2. 进程已退出

bash 复制代码
# 进程退出后查看
$ pmap 99999
99999: No such process

# 解决方案:先确认进程是否存在
ps -p 99999 > /dev/null 2>&1 && pmap 99999 || echo "进程不存在"

3. 输出过长导致截断

bash 复制代码
# 对于大型进程(如 Java/数据库),输出可能非常长
pmap -x $(pgrep java) | wc -l

# 建议分页查看或过滤
pmap -x $(pgrep java) | less
pmap -x $(pgrep java) | grep -E "heap|stack|anon"

4. 读取 smaps 很慢

bash 复制代码
# -XX 选项需要读取 smaps,对于内存大且映射多的进程会很慢
# 例如:一个 Java 进程可能有数千个内存映射

# 替代方案:使用 -x 而非 -XX
time pmap -x $(pgrep java)    # 较快
time pmap -XX $(pgrep java)   # 可能耗时数秒

📝 自动化脚本

内存泄漏监控脚本

bash 复制代码
#!/bin/bash
# pmap_mem_watch.sh - 监控进程内存变化趋势
# 用法: ./pmap_mem_watch.sh <PID> [检测次数] [间隔秒数]

PID=$1
CHECKS=${2:-60}
INTERVAL=${3:-10}

if [ -z "$PID" ]; then
    echo "用法: $0 <PID> [检测次数] [间隔秒数]"
    exit 1
fi

# 检查进程是否存在
if ! kill -0 $PID 2>/dev/null; then
    echo "错误: 进程 $PID 不存在"
    exit 1
fi

echo "开始监控 PID=$PID,共 $CHECKS 次,间隔 ${INTERVAL}s"
echo "时间              | 虚拟内存(KB) | 物理内存(KB) | 脏页(KB) "
echo "------------------+-------------+-------------+----------"

for i in $(seq 1 $CHECKS); do
    if ! kill -0 $PID 2>/dev/null; then
        echo "$(date '+%Y-%m-%d %H:%M:%S') | 进程已退出"
        break
    fi
    stats=$(pmap -x $PID 2>/dev/null | tail -1 | awk '{printf "%s | %s | %s", $2, $3, $4}')
    printf "%s | %s\n" "$(date '+%Y-%m-%d %H:%M:%S')" "$stats"
    sleep $INTERVAL
done

查找匿名内存占用最大的进程

bash 复制代码
#!/bin/bash
# 列出当前系统中匿名内存(anon)占用最多的前10个进程

echo "匿名内存 Top 10 进程:"
echo "PID       进程名             匿名内存(KB)"
echo "--------  ------------------  -------------"

for pid_dir in /proc/[0-9]*; do
    pid=$(basename $pid_dir)
    # 跳过内核线程(comm 为空的情况)
    [ -f "$pid_dir/comm" ] || continue
    comm=$(tr '\0' ' ' < "$pid_dir/comm" 2>/dev/null)
    # 计算匿名内存总量
    anon_kb=$(awk '/\[anon\]/ {sum+=$2} END {print sum+0}' "$pid_dir/smaps" 2>/dev/null)
    if [ "${anon_kb:-0}" -gt 0 ]; then
        printf "%-8s  %-18s  %s\n" "$pid" "${comm:0:18}" "$anon_kb"
    fi
done | sort -k3 -n -r | head -10

📚 总结

pmap 是 Linux 系统中 进程内存分析的利器。它能清晰展示进程的虚拟地址空间分布,帮助运维人员和开发者快速定位内存泄漏、分析内存占用结构、评估进程的内存健康度。相比直接阅读 /proc/<pid>/maps/proc/<pid>/smapspmap 提供了更友好的格式化输出和汇总统计。

在日常工作中,pmappstopsmem 等工具配合使用,可以构成完整的内存监控体系。对于排查 Java 堆外内存、Node.js Buffer 内存、数据库缓存区等场景,pmap-x 扩展输出尤为实用。

相关推荐
未来侦察班1 小时前
网络协议 网络层,万物归于IP
网络·网络协议·协议·ip·网络层·tcpip
郝学胜-神的一滴1 小时前
CMake 017:彩色日志输出实战
linux·c语言·开发语言·c++·软件工程·软件构建·cmake
暗影天帝1 小时前
BPI-R3 Mini NAND 刷机教程(Webfailsafe 方案)
linux
luj_17681 小时前
FreeDOS vs MS-DOS PC-DOS 对比解析
服务器·c语言·开发语言·经验分享·算法
非凡大爹2 小时前
实验十三 华为三层交换机实现 VLAN 间通信实验指导书
网络·计算机网络·华为
Full Stack Developme2 小时前
Linux rm-rf 执行后,硬盘空间变化
linux·运维·服务器
插件开发2 小时前
vs2015 cuda c++ cdpSimplePrint范例,递归功能实现演示
linux·c++·算法
java知路2 小时前
centos euler离线下载docker镜像
linux·docker·centos
沪漂阿龙2 小时前
LangChain 系列之Agent:从固定流程到模型自主决策
服务器·数据库·langchain