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文件。
具体原因如下:
- 脚本代码明确使用
dd if="/proc/$pid/mem",读取的是你用mmap分配后填充的随机可打印ASCII字符——输出右侧的ASCII解释(比如pm.{gbo、rp.h..c.oh)完全匹配C代码里的填充逻辑。 - 你参考的内核文档是针对
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
相关产品推荐
相关产品推荐

