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

Android虚拟内存到物理内存转换的异常输出排查

问题:读取/proc/pid/mem输出不符合pagemap文档预期

环境配置

通过Android Studio使用adb shell+su操作,先读取/proc/pid/maps获取mmap区域的内存范围,再运行print_pagemap.sh并传入pid、起始和结束地址。内存通过mmap分配并填充随机可打印ASCII字符,代码如下:

void* ptr = mmap(NULL, mmapSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0);

if (ptr == NULL) {
    // 内存分配失败,ptr不是有效虚拟地址
    LOGI("malloc failed");
    return NULL;
} else {
    // 内存分配成功,ptr是有效虚拟地址
    LOGI("Virtual address: %p", ptr);
}
// 用随机字符填充整个mmap区域
for (size_t i = 0; i < mmapSize; ++i) {
    ((char*)ptr)[i] = 'A' + rand() % 95 + 32; // 生成可打印ASCII字符
}

问题描述

我得到的十六进制内存段输出不符合内核文档中pagemap的预期:部分条目第63位未设为1,且第57-60位不为0。已知系统是小端序,脚本输出也应为小端序,为何会出现这种情况?是不是我误解了输出内容?

脚本执行输出

redfin:/data/local/tmp # ./print_pagemap.sh 24708 0x7c437ae000 0x7c437ae100                                                                                                                                                        
00000000: a69cb9b4aa6b8270 76918d87b271ad80 8889706d8f7b6762 6f8f8b899a676380  .....k.pv....q....pm.{gbo....gc.
00000020: 9fbb728a63939679 61639cb070867097 ae7fa47b96a77ca2 76a36bafa66e6d86  ..r.c..yac..p.p....{..|.v.k..nm.
00000040: 667bac66ad827eaa 81b69a907da66768 62a77f958e9b77a1 7b7e9062889c848b  f{.f..~.....}.ghb.....w.{~.b....
00000060: b67090a091aa88b1 a0be7eb9a581bea3 697d7893b48e746d a9a16b717a8e9b6e  .p........~.....i}x...tm..kqz..n
00000080: 9d68ad6bb175b88f 717685b593809898 9dad6c8e7b7c9a61 b9a172746fa981a8  .h.k.u..qv........l.{|.a..rto...
000000a0: ae6bb29c7da76b8d b98d7f8da9b66583 a36db1bb8688b87f 6a679278b0af619e  .k..}.k.......e..m......jg.x..a.
000000c0: baaf7a749482a08d abbeb694b1bbb792 68a58a8d6d82a976 897b8b766888b062  ..zt............h...m..v.{.vh..b
000000e0: 746a75a788b17270 af68a19d63986f68 7d959186b77a9c7d 926792998e82979e  tju...rp.h..c.oh}....z.}.g......

脚本内容

#!/system/bin/sh

# 确保传入三个参数
if [ "$#" -ne 3 ]; then
    echo "Usage: $0 <pid> <start_address> <end_address>"
    exit 1
fi

# 提取参数
pid="$1"
start_address="$2"
end_address="$3"

# 将十六进制地址转为十进制
start_address_dec=$(printf "%d" $start_address)
end_address_dec=$(printf "%d" $end_address)

# 使用dd读取地址范围内的数据
dd if="/proc/$pid/mem" bs=1 skip=$start_address_dec count=$(($end_address_dec - $start_address_dec)) status=none | xxd -g 8 -c 32 | grep -v '0000000000000000 0000000000000000 0000000000000000 0000000000000000'

回答

你的核心误解是:脚本读取的是/proc/$pid/mem(进程的虚拟内存内容),而非/proc/$pid/pagemap文件。

具体原因如下:

  1. 脚本代码明确使用dd if="/proc/$pid/mem",读取的是你用mmap分配后填充的随机可打印ASCII字符——输出右侧的ASCII解释(比如pm.{gbo、rp.h..c.oh)完全匹配C代码里的填充逻辑。
  2. 你参考的内核文档是针对pagemap文件的,该文件存储进程虚拟页与物理页的映射关系,每个条目是64位且有固定位定义(第63位表示页是否存在,57-60位是页类型等)。但你根本没读取这个文件,输出自然不符合pagemap的格式要求。

如果要读取pagemap信息,需要修改脚本:

  • 计算虚拟地址对应的页索引:page_index=$((start_address_dec / 4096))(假设系统页大小为4096字节)
  • 读取/proc/$pid/pagemap中对应位置的条目:dd if="/proc/$pid/pagemap" bs=8 skip=$page_index count=$(( (end_address_dec - start_address_dec)/4096 ))
  • 再用xxd解析这些64位条目,就能得到符合文档预期的结构。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 10:04:52