当查看 /proc/pid/maps 中的内存映射时,我们可以看到不同长度的地址:
00400000-0042e000 r-xp 00000000 fb:01 268953 /bin/nano
0062e000-0062f000 r--p 0002e000 fb:01 268953 /bin/nano
0062f000-00630000 rw-p 0002f000 fb:01 268953 /bin/nano
0081e000-00906000 rw-p 00000000 00:00 0 [heap]
7f8313e5c000-7f8314109000 rw-p 00000000 fb:01 2399989 /usr/share/misc/magic.mgc
7f8314109000-7f83142ce000 r--p 00000000 fb:01 2759354 /usr/lib64/locale/locale-archive
7f83142ce000-7f83142d1000 r-xp 00000000 fb:01 1457046 /lib64/libdl-2.17.so
7f83142d1000-7f83144d0000 ---p 00003000 fb:01 1457046 /lib64/libdl-2.17.so
我们有 8 位长度的地址,例如:
- 00400000-0042e000
以及 12 位长度的(最后 3 位始终为 0):
- 7f8313e5c000-7f8314109000
为什么这些地址采用这种格式?我可以将它们转换为 8 位长度吗?
答案1
首先,您无法将地址转换为只有 8 位数字。内存地址可以并且将会具有比仅用 8 位数字表示的值大得多的值。
/proc/pid/maps
内存地址按原样表示的原因位于最新内核源代码树中fs/proc/task_mmu.c
(或) 中的第 283 行:task_nommu.c
283 seq_printf(m, "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu ",
284 start,
285 end,
286 flags & VM_READ ? 'r' : '-',
287 flags & VM_WRITE ? 'w' : '-',
288 flags & VM_EXEC ? 'x' : '-',
289 flags & VM_MAYSHARE ? 's' : 'p',
290 pgoff,
291 MAJOR(dev), MINOR(dev), ino);
这归结为,在任何十六进制字符串表示形式短于 8 位数字的内存地址中,都将用前导零填充。任何大于该值的值都将按原样表示,不会被截断为 8 位数字。这就是printk()
printf 样式格式化的工作方式。
现在从这一切中可以得到什么?也许您应该花一点时间思考为什么要将内存地址截断为 8 位数字。您认为这样做有什么好处?
答案2
这些地址是它们应该的地址,您不应该将它们视为可以截断的内容。查看手册页man proc
并阅读该文件的内容/proc/<pid>/maps
,以了解有关这些列含义的更多信息。
摘抄
/proc/[pid]/maps
A file containing the currently mapped memory regions and their
access permissions. See mmap(2) for some further information
about memory mappings.
The format of the file is:
address perms offset dev inode pathname
00400000-00452000 r-xp 00000000 08:02 173521 /usr/bin/dbus-daemon
00651000-00652000 r--p 00051000 08:02 173521 /usr/bin/dbus-daemon
00652000-00655000 rw-p 00052000 08:02 173521 /usr/bin/dbus-daemon
00e03000-00e24000 rw-p 00000000 00:00 0 [heap]
00e24000-011f7000 rw-p 00000000 00:00 0 [heap]
...
35b1800000-35b1820000 r-xp 00000000 08:02 135522 /usr/lib64/ld-2.15.so
35b1a1f000-35b1a20000 r--p 0001f000 08:02 135522 /usr/lib64/ld-2.15.so
地址以这种方式显示是因为此输出中未显示前导零。您可以使用awk
我构建的这个粗略命令,它将正确的前导零放回到输出中,以便所有内容都显示与考虑到地址的全部地址空间量对齐。
$ awk '{split($1,a,"-"); $1=sprintf("%16s-%16s",a[1],a[2]); gsub(/ /,"0",$1); \
printf "%33s %4s %8s %5s %-6s %7s\n",$1,$2,$3,$4,$5,$6}' \
/proc/<pid>/maps
例子
这是我的系统中的示例地图文件。
前
$ tail -10 /proc/1607/maps
7f6c3a247000-7f6c3a248000 rw-p 00021000 fd:01 526702 /usr/lib64/ld-2.17.so
7f6c3a248000-7f6c3a249000 rw-p 00000000 00:00 0
7f6c3a249000-7f6c3a2ad000 r-xp 00000000 fd:01 529820 /usr/bin/dbus-daemon
7f6c3a4ac000-7f6c3a4ae000 r--p 00063000 fd:01 529820 /usr/bin/dbus-daemon
7f6c3a4ae000-7f6c3a4af000 rw-p 00065000 fd:01 529820 /usr/bin/dbus-daemon
7f6c3a68a000-7f6c3a6ab000 rw-p 00000000 00:00 0 [heap]
7f6c3a6ab000-7f6c3aace000 rw-p 00000000 00:00 0 [heap]
7fffce239000-7fffce25a000 rw-p 00000000 00:00 0 [stack]
7fffce3fe000-7fffce400000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
后
$ awk '{split($1,a,"-"); $1=sprintf("%16s-%16s",a[1],a[2]); gsub(/ /,"0",$1); \
printf "%33s %4s %8s %5s %-6s %7s\n",$1,$2,$3,$4,$5,$6}' \
<(tail /proc/1607/maps)
00007f6c3a247000-00007f6c3a248000 rw-p 00021000 fd:01 526702 /usr/lib64/ld-2.17.so
00007f6c3a248000-00007f6c3a249000 rw-p 00000000 00:00 0
00007f6c3a249000-00007f6c3a2ad000 r-xp 00000000 fd:01 529820 /usr/bin/dbus-daemon
00007f6c3a4ac000-00007f6c3a4ae000 r--p 00063000 fd:01 529820 /usr/bin/dbus-daemon
00007f6c3a4ae000-00007f6c3a4af000 rw-p 00065000 fd:01 529820 /usr/bin/dbus-daemon
00007f6c3a68a000-00007f6c3a6ab000 rw-p 00000000 00:00 0 [heap]
00007f6c3a6ab000-00007f6c3aace000 rw-p 00000000 00:00 0 [heap]
00007fffce239000-00007fffce25a000 rw-p 00000000 00:00 0 [stack]
00007fffce3fe000-00007fffce400000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]