/proc/[pid]/maps文件格式解析

/proc/[pid]/maps文件格式解析

以下内容摘录在man手册,可以通过运行命令(man 5 proc)获得。

/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
35b1a20000-35b1a21000 rw-p 00020000 08:02 135522  /usr/lib64/ld-2.15.so
35b1a21000-35b1a22000 rw-p 00000000 00:00 0
35b1c00000-35b1dac000 r-xp 00000000 08:02 135870  /usr/lib64/libc-2.15.so
35b1dac000-35b1fac000 ---p 001ac000 08:02 135870  /usr/lib64/libc-2.15.so
35b1fac000-35b1fb0000 r--p 001ac000 08:02 135870  /usr/lib64/libc-2.15.so
35b1fb0000-35b1fb2000 rw-p 001b0000 08:02 135870  /usr/lib64/libc-2.15.so
...
f2c6ff8c000-7f2c7078c000 rw-p 00000000 00:00 0    [stack:986]
...
7fffb2c0d000-7fffb2c2e000 rw-p 00000000 00:00 0   [stack]
7fffb2d48000-7fffb2d49000 r-xp 00000000 00:00 0   [vdso]

The address field is the address space in the process that the mapping occupies.  
address部分显示的是该段映射的虚拟地址。

The perms field is a set of permissions:

r = read
w = write
x = execute
s = shared
p = private (copy on write)
权限分为rwxsp,r可读、w可写、x可执行、s共享、p私有或者(写时拷贝)。

The  offset  field is the offset into the file/whatever; dev is the device (major:minor); inode is the inode on that device.  0 indicates that no inode is associated with the memory region, as would be the case with BSS (uninitialized data).
offset是指该段映射在文件/其它设备上的偏移量。inode是指文件或者设备的inode节点,0表示没有inode与该段内存关联。


The pathname field will usually be the file that is backing the mapping.  For ELF files, you can easily coordinate with the offset field by looking at the Offset field in the ELF program headers (readelf -l).

路径名是与该段内存关联的文件路径。对于ELF文件,可以通过realelf-l在offset字段和ELF文件头部OFFSET字段同步。

There are additional helpful pseudo-paths:

[stack] The initial process's (also known as the main thread's) stack.
主线程/进程堆栈
[stack:<tid>] (since Linux 3.4) A thread's stack (where the <tid> is a thread ID).  It corresponds to the /proc/[pid]/task/[tid]/ path.
线程堆栈。
[vdso] The virtual dynamically linked shared object.
内核虚拟动态映射对象
[heap] The process's heap.
进程的堆空间。

If  the  pathname  field is blank, this is an anonymous mapping as obtained via the mmap(2) function.  There is no easy way to coordinate this back to a process's source, short of running it through gdb(1), strace(1), or similar.
如果pathname为空,那么是通过mmap函数进行的匿名映射。没有简单的方法获得该字段,需要查看代码,或者gdb、strace等类似的方法。

Under Linux 2.0 there is no field giving pathname.
​```

各共享库的代码段,存放着二进制可执行的机器指令,是由kernel把该库ELF文件的代码段map到虚存空间;
各共享库的数据段,存放着程序执行所需的全局变量,是由kernel把ELF文件的数据段map到虚存空间;
用户代码段,存放着二进制形式的可执行的机器指令,是由kernel把ELF文件的代码段map到虚存空间;
用户数据段之上是代码段,存放着程序执行所需的全局变量,是由kernel把ELF文件的数据段map到虚存空间;
用户数据段之下是堆(heap),当且仅当malloc调用时存在,是由kernel把匿名内存map到虚存空间,堆则在程序中没有调用malloc的情况下不存在;
用户数据段之下是栈(stack),作为进程的临时数据区,是由kernel把匿名内存map到虚存空间,栈空间的增长方向是从高地址到低地址。
 

     Proc/pid/maps显示进程映射了的内存区域和访问权限。对应内核中的操作集为proc_pid_maps_op,具体的导出函数为show_map。内核中进程的一段地址空间用一个vm_area_struct结构体表示,所有地址空间存储在task->mm->mmap链表中。

    一个文件可以映射到进程的一段内存区域中,映射的文件描述符保存在vm_area_struct->vm_file域中,这种内存区域叫做有名内存区域,相反,属于匿名映射内存区域。Vm_area_struct每项对应解析如下表所示:

下面一起看下一个proc maps的例子。

cat /proc/19970/task/19970/maps

001f7000-00212000 r-xp 00000000 fd:00 2719760    /lib/ld-2.5.so

00212000-00213000 r-xp 0001a000 fd:00 2719760    /lib/ld-2.5.so

00213000-00214000 rwxp 0001b000 fd:00 2719760    /lib/ld-2.5.so

00214000-0036b000 r-xp 00000000 fd:00 2719767    /lib/libc-2.5.so

0036b000-0036d000 r-xp 00157000 fd:00 2719767    /lib/libc-2.5.so

0036d000-0036e000 rwxp 00159000 fd:00 2719767    /lib/libc-2.5.so

0036e000-00371000 rwxp 0036e000 00:00 0

0054f000-00565000 r-xp 00000000 fd:00 2719791    /lib/libpthread-2.5.so

00565000-00566000 r-xp 00015000 fd:00 2719791    /lib/libpthread-2.5.so

00566000-00567000 rwxp 00016000 fd:00 2719791    /lib/libpthread-2.5.so

00567000-00569000 rwxp 00567000 00:00 0

006f5000-006f6000 r-xp 006f5000 00:00 0          [vdso]

08048000-08049000 r-xp 00000000 fd:00 3145810    /home/lijz/code/pthread

08049000-0804a000 rw-p 00000000 fd:00 3145810    /home/lijz/code/pthread

08c50000-08c71000 rw-p 08c50000 00:00 0          [heap]

b75d7000-b75d8000 ---p b75d7000 00:00 0

b75d8000-b7fda000 rw-p b75d8000 00:00 0

b7fe4000-b7fe5000 rw-p b7fe4000 00:00 0

bf987000-bf99c000 rw-p bffea000 00:00 0          [stack]

      进程的每段地址空间由struct vm_area_struct 描述。如上所示的每一行对应一个vm_area_struct结构体。一个文件可以映射到内存中,vm_area_struct的vm_file保存了文件描述符,这种映射称为有名映射,反之则为匿名映射。下面以第十四行为例,解释各例的内容。

第一列:08049000-0804a000-----本段内存映射的虚拟地址空间范围,对应vm_area_struct中的vm_start和vm_end。

第二列:rw-p----权限 r-读,w-写 x-可执行 p-私有,对应vm_flags。

第三列:00000000----针对有名映射,指本段映射地址在文件中的偏移,对应vm_pgoff。对匿名映射而言,为vm_area_struct->vm_start。

第四列:fd:00----所映射的文件所属设备的设备号,对应vm_file->f_dentry->d_inode->i_sb->s_dev。匿名映射为0。其中fd为主设备号,00为次设备号。

第五列:3145810----文件的索引节点号,对应vm_file->f_dentry->d_inode->i_ino,与ls –i显示的内容相符。匿名映射为0。

第六列:/home/lijz/code/pthread---所映射的文件名。对有名映射而言,是映射的文件名,对匿名映射来说,是此段内存在进程中的作用。[stack]表示本段内存作为栈来使用,[heap]作为堆来使用,其他情况则为无。

       经过上面的分析,proc maps中的每一列代表的意思已经非常清晰了。接下来看下proc每maps中每一行的解析。各共享库的代码段,存放着二进制可执行的机器指令,由kernel把该库ELF文件的代码段map到虚存空间;各共享库的数据段,存放着程序执行所需的全局变量,由kernel把ELF文件的数据段map到虚存空间;用户代码段,存放着二进制形式的可执行的机器指令,由kernel把ELF文件的代码段map到虚存空间;用户数据段,存放着程序执行所需的全局变量,由kernel把ELF文件的数据段map到虚存空间;堆(heap),当且仅当malloc调用时存在,由kernel把匿名内存map到虚存空间,堆则在程序中没有调用malloc的情况下不存在;栈(stack),作为进程的临时数据区,由kernel把匿名内存map到虚存空间,栈空间的增长方向是从高地址到低地址。

pthread这个应用程序在maps中占用了两行,内容如下:

08048000-08049000 r-xp 00000000 fd:00 3145810    /home/lijz/code/pthread

08049000-0804a000 rw-p 00000000 fd:00 3145810    /home/lijz/code/pthread

  其中第一行的权限是只读,并且可执行,说明第一行是应用程序的代码段,而第二行的权限是可读可写,但是没有执行权限,说明该段是pthread的数据段。

00c56000-00dad000 r-xp 00000000 fd:00 2719767    /lib/libc-2.5.so

00dad000-00daf000 r-xp 00157000 fd:00 2719767    /lib/libc-2.5.so

00daf000-00db0000 rwxp 00159000 fd:00 2719767    /lib/libc-2.5.so

以上是libc-2.5共享库在maps文件中的记录,每个共享库在maps文件中对应着三行,分别是数据段与代码段。

堆[heap]段。

08c64000-08c85000 rw-p 08c64000 00:00 0          [heap]

      有些maps文件并不会出现该记录,这主要跟程序中有无使用malloc相关,如果主线程使用了malloc就会有该记录,否则就没有。在子线程中调用malloc,会产生另外的堆映射,但是并不会标记[heap]。例如,在子线程中动态分配1MB的内存空间,pthread2应用程序的执行结果如下所示:

tid addr 0xbfd818f0

child thread run

stackbase 0xb7f4f3c0

stackaddr =0x7754e008----malloc分配的地址

guardsize 4096

对应的maps文件:

08048000-08049000 r-xp 00000000 fd:00 3145811    /home/lijz/code/pthread2

08049000-0804a000 rw-p 00000000 fd:00 3145811    /home/lijz/code/pthread2

0945a000-0947b000 rw-p 0945a000 00:00 0          [heap]

7754e000-b754f000 rw-p 7754e000 00:00 0 -----------区间大小正是1MB

b754f000-b7550000 ---p b754f000 00:00 0

b7550000-b7f52000 rw-p b7550000 00:00 0

b7f5c000-b7f5d000 rw-p b7f5c000 00:00 0

bfd6e000-bfd83000 rw-p bffea000 00:00 0          [stack]

    maps文件中红色标注的行,从内容上看,本段内存大小是1MB,权限为读写私有,偏移为本段内存的开始地址,设备号和文件索引节点为0。可以看出本段内存是进程通过mmap映射的一段空间,是匿名映射。在pthread2程序中,正好用malloc分配了一个1MB的内存,能够与这段内存对应。同时,malloc分配的地址0x7754e008正落在该区间,并且偏向区间低地址部分,说明该区间是个堆地址空间。说明了这段1M的内存确实是进程调用malloc分配的,其中malloc又调用mmap系统调用匿名映射。

栈段[stack],下面用几个例子来说明栈段。

bfd50000-bfd65000 rw-p bffea000 00:00 0          [stack]

对于单线程应用程序而言,只有一个[stack]段,对应多线程应用程序,[stack]段是主线程的栈空间,子线程的栈空间则用pthread库自动分配。

 例1,将一个单线程的应用的局部变量的地址打印出来,执行的结果如下所示:

 ./pthread2

tid addr 0xbfc73600

对应的maps文件:

08048000-08049000 r-xp 00000000 fd:00 3145811    /home/lijz/code/pthread2

08049000-0804a000 rw-p 00000000 fd:00 3145811    /home/lijz/code/pthread2

b7f7e000-b7f80000 rw-p b7f7e000 00:00 0

b7f8a000-b7f8b000 rw-p b7f8a000 00:00 0

bfc5f000-bfc74000 rw-p bffea000 00:00 0          [stack]

局部变量的地址0xbfc73600在[stack]区间。

例2:将一个拥有一个子线程的应用局部变量打印出来,执行的结果如下所示:

tid addr 0xbfd64740---------主线程中打印的局部变量地址

child thread run

stackaddr   0xb7fc93c4--------子线程中打印的局部变量地址

guardsize 4096---------栈保护页大小

对应的maps文件如下:

08048000-08049000 r-xp 00000000 fd:00 3145811    /home/lijz/code/pthread2

08049000-0804a000 rw-p 00000000 fd:00 3145811    /home/lijz/code/pthread2

08c64000-08c85000 rw-p 08c64000 00:00 0          [heap]

b75c9000-b75ca000 ---p b75c9000 00:00 0---------pthread_create默认的栈溢出保护区

b75ca000-b7fcc000 rw-p b75ca000 00:000------------pthread_create创建的子线程的栈空间

b7fd6000-b7fd7000 rw-p b7fd6000 00:00 0------------------4KB应该也是通过mmap产生的匿名映射

bfd50000-bfd65000 rw-p bffea000 00:00 0          [stack]---------主进程的栈空间

       由上执行结果显示,主线程中局部变量地址0xbfd64740落在[stack]区间,而子线程局部变量地址0xb7fc93c4则落在b75ca000-b7fcc000 rw-p b75ca00区间,并且局部变量的地址从高地址开始分配,说明该VMA正是子线程的栈地址空间。另外,对栈空间,pthread默认设置了一个4KB的栈保护页,对应的区间为:b75c9000-b75ca000---p b75c9000,该区间不可读,不可写,也不能执行,通过这些属性信息的设置,可以达到栈溢出保护的作用。

例3:在例2的基础上,多创建一个线程,pthread2程序的执行结果如下所示:

./pthread2

tid addr 0xbfc81610----------主线程局部变量地址

child thread run

stackaddr = 0xb7f183c0-------子线程1局部变量地址

guardsize 4096

child thread2 run

stackaddr =0xb75173c4 ----------子线程局部变量地址

guardsize 4096

对应的maps文件:

08048000-08049000 r-xp 00000000 fd:00 3145811    /home/lijz/code/pthread2

08049000-0804a000 rw-p 00000000 fd:00 3145811    /home/lijz/code/pthread2

092d6000-092f7000 rw-p 092d6000 00:00 0          [heap]

76b16000-b6b17000 rw-p 76b16000 00:00 0 ----------mallocmmap

b6b17000-b6b18000 ---p b6b17000 00:00 0

b6b18000-b7518000 rw-p b6b18000 00:000---------pthread thread2 stack space

b7518000-b7519000 ---p b7518000 00:00 0                                      

b7519000-b7f1b000 rw-p b7519000 00:000----------pthread thread1 stack space

b7f25000-b7f26000 rw-p b7f25000 00:00 0

bfc6e000-bfc83000 rw-p bffea000 00:00 0          [stack]---main thread stack space

 从maps文件记录上看,增加一个子线程,在maps文件中就增加了两条记录,分别是子线程的栈空间和栈保护页的记录。默认情况下,pthread为子线程预留的栈空间大小为1MB,栈保护页为4KB(这主要跟页大小相关)。

  总之,proc maps文件可以查看进程的内存映射,每一段内存的权限属性等信息。
 

猜你喜欢

转载自blog.csdn.net/wxh0000mm/article/details/88421015