我的基本问题是为什么64位进程的VSIZE比为32位编译的完全相同的程序大得多?
以下是32位进程的/ proc /< pid> / maps文件的输出。
00148000-00149000 r-xp 00000000 00:00 0 [vdso]
00149000-002d2000 r-xp 00000000 fd:02 8914142 /lib/libc-2.12.so
002d2000-002d3000 ---p 00189000 fd:02 8914142 /lib/libc-2.12.so
002d3000-002d5000 r--p 00189000 fd:02 8914142 /lib/libc-2.12.so
002d5000-002d6000 rw-p 0018b000 fd:02 8914142 /lib/libc-2.12.so
002d6000-002d9000 rw-p 00000000 00:00 0
005c9000-005da000 r-xp 00000000 fd:02 17059392 /tmp/vsizetest/lib/libtesting.so
005da000-005db000 rw-p 00010000 fd:02 17059392 /tmp/vsizetest/lib/libtesting.so
005db000-0061b000 rw-p 00000000 00:00 0
00661000-00689000 r-xp 00000000 fd:02 8917713 /lib/libm-2.12.so
00689000-0068a000 r--p 00027000 fd:02 8917713 /lib/libm-2.12.so
0068a000-0068b000 rw-p 00028000 fd:02 8917713 /lib/libm-2.12.so
00694000-006ab000 r-xp 00000000 fd:02 8917680 /lib/libpthread-2.12.so
006ab000-006ac000 r--p 00016000 fd:02 8917680 /lib/libpthread-2.12.so
006ac000-006ad000 rw-p 00017000 fd:02 8917680 /lib/libpthread-2.12.so
006ad000-006af000 rw-p 00000000 00:00 0
006e5000-00703000 r-xp 00000000 fd:00 3150403 /lib/ld-2.12.so
00703000-00704000 r--p 0001d000 fd:00 3150403 /lib/ld-2.12.so
00704000-00705000 rw-p 0001e000 fd:00 3150403 /lib/ld-2.12.so
00983000-009a0000 r-xp 00000000 fd:02 8914997 /lib/libgcc_s-4.4.5-20110214.so.1
009a0000-009a1000 rw-p 0001d000 fd:02 8914997 /lib/libgcc_s-4.4.5-20110214.so.1
00ca5000-00d86000 r-xp 00000000 fd:02 6300601 /usr/lib/libstdc++.so.6.0.13
00d86000-00d8a000 r--p 000e0000 fd:02 6300601 /usr/lib/libstdc++.so.6.0.13
00d8a000-00d8c000 rw-p 000e4000 fd:02 6300601 /usr/lib/libstdc++.so.6.0.13
00d8c000-00d92000 rw-p 00000000 00:00 0
08048000-08049000 r-xp 00000000 fd:02 21134666 /tmp/vsizetest/bin/testvsz
08049000-0804a000 rw-p 00000000 fd:02 21134666 /tmp/vsizetest/bin/testvsz
09b8d000-09bae000 rw-p 00000000 00:00 0 [heap]
f7796000-f779c000 rw-p 00000000 00:00 0
ff998000-ff9ae000 rw-p 00000000 00:00 0 [stack]
这导致总VSIZE为3656.
以下是64位进程的/ proc /< pid> / maps文件的输出。
00400000-00401000 r-xp 00000000 fd:02 21134667 /tmp/vsizetest/bin64/testvsz
00600000-00601000 rw-p 00000000 fd:02 21134667 /tmp/vsizetest/bin64/testvsz
02301000-02322000 rw-p 00000000 00:00 0 [heap]
3b7c800000-3b7c820000 r-xp 00000000 fd:00 661349 /lib64/ld-2.12.so
3b7ca1f000-3b7ca20000 r--p 0001f000 fd:00 661349 /lib64/ld-2.12.so
3b7ca20000-3b7ca21000 rw-p 00020000 fd:00 661349 /lib64/ld-2.12.so
3b7ca21000-3b7ca22000 rw-p 00000000 00:00 0
3b7cc00000-3b7cd86000 r-xp 00000000 fd:00 661350 /lib64/libc-2.12.so
3b7cd86000-3b7cf86000 ---p 00186000 fd:00 661350 /lib64/libc-2.12.so
3b7cf86000-3b7cf8a000 r--p 00186000 fd:00 661350 /lib64/libc-2.12.so
3b7cf8a000-3b7cf8b000 rw-p 0018a000 fd:00 661350 /lib64/libc-2.12.so
3b7cf8b000-3b7cf90000 rw-p 00000000 00:00 0
3b7d000000-3b7d083000 r-xp 00000000 fd:00 661365 /lib64/libm-2.12.so
3b7d083000-3b7d282000 ---p 00083000 fd:00 661365 /lib64/libm-2.12.so
3b7d282000-3b7d283000 r--p 00082000 fd:00 661365 /lib64/libm-2.12.so
3b7d283000-3b7d284000 rw-p 00083000 fd:00 661365 /lib64/libm-2.12.so
3b7d800000-3b7d817000 r-xp 00000000 fd:00 661352 /lib64/libpthread-2.12.so
3b7d817000-3b7da16000 ---p 00017000 fd:00 661352 /lib64/libpthread-2.12.so
3b7da16000-3b7da17000 r--p 00016000 fd:00 661352 /lib64/libpthread-2.12.so
3b7da17000-3b7da18000 rw-p 00017000 fd:00 661352 /lib64/libpthread-2.12.so
3b7da18000-3b7da1c000 rw-p 00000000 00:00 0
3b7e000000-3b7e007000 r-xp 00000000 fd:00 661361 /lib64/librt-2.12.so
3b7e007000-3b7e206000 ---p 00007000 fd:00 661361 /lib64/librt-2.12.so
3b7e206000-3b7e207000 r--p 00006000 fd:00 661361 /lib64/librt-2.12.so
3b7e207000-3b7e208000 rw-p 00007000 fd:00 661361 /lib64/librt-2.12.so
3b87000000-3b87016000 r-xp 00000000 fd:00 664219 /lib64/libgcc_s-4.4.6-20110824.so.1
3b87016000-3b87215000 ---p 00016000 fd:00 664219 /lib64/libgcc_s-4.4.6-20110824.so.1
3b87215000-3b87216000 rw-p 00015000 fd:00 664219 /lib64/libgcc_s-4.4.6-20110824.so.1
3d44c00000-3d44ce8000 r-xp 00000000 fd:00 3019214 /usr/lib64/libstdc++.so.6.0.13
3d44ce8000-3d44ee8000 ---p 000e8000 fd:00 3019214 /usr/lib64/libstdc++.so.6.0.13
3d44ee8000-3d44eef000 r--p 000e8000 fd:00 3019214 /usr/lib64/libstdc++.so.6.0.13
3d44eef000-3d44ef1000 rw-p 000ef000 fd:00 3019214 /usr/lib64/libstdc++.so.6.0.13
3d44ef1000-3d44f06000 rw-p 00000000 00:00 0
7f30ab397000-7f30ab39c000 rw-p 00000000 00:00 0
7f30ab39c000-7f30ab3ad000 r-xp 00000000 fd:02 21127804 /tmp/vsizetest/lib64/libtesting.so
7f30ab3ad000-7f30ab5ac000 ---p 00011000 fd:02 21127804 /tmp/vsizetest/lib64/libtesting.so
7f30ab5ac000-7f30ab5ad000 rw-p 00010000 fd:02 21127804 /tmp/vsizetest/lib64/libtesting.so
7f30ab5ad000-7f30ab5ee000 rw-p 00000000 00:00 0
7f30ab606000-7f30ab609000 rw-p 00000000 00:00 0
7fff69512000-7fff69528000 rw-p 00000000 00:00 0 [stack]
7fff695ff000-7fff69600000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
导致VSIZE为18480。
两个地图之间的主要区别是64位数据中的以下条目:
3b7cd86000-3b7cf86000 ---p 00186000 fd:00 661350 /lib64/libc-2.12.so
3b7d083000-3b7d282000 ---p 00083000 fd:00 661365 /lib64/libm-2.12.so
3b7d817000-3b7da16000 ---p 00017000 fd:00 661352 /lib64/libpthread-2.12.so
3b7e007000-3b7e206000 ---p 00007000 fd:00 661361 /lib64/librt-2.12.so
3b87016000-3b87215000 ---p 00016000 fd:00 664219 /lib64/libgcc_s-4.4.6-20110824.so.1
3d44ce8000-3d44ee8000 ---p 000e8000 fd:00 3019214 /usr/lib64/libstdc++.so.6.0.13
7f30ab3ad000-7f30ab5ac000 ---p 00011000 fd:02 21127804 /tmp/vsizetest/lib64/libtesting.so
18480 VSIZE中14316的帐号。
其他程序的其他实验似乎表明,在64位中,您似乎为进程使用的每个共享库获取了这些私有的,不可读的,不可写的,不可执行的内存块之一,而在32位中几乎没有这些块。
有谁知道这些内存是什么?
注意:根据类似问题What these memory regions for, from a Linux process?的一些答案,这不是一个多线程进程,它已经编译为-fPIC。
答案 0 :(得分:8)
VSIZE的主要区别在于如何在32位和64位版本的情况下完成共享库的PROT_NONE映射(模式“--- p”)。
这些正是您发现产生差异的映射。
通常,对于每个加载的共享库,我们将有四个映射:
3b7cc00000-3b7cd86000 r-xp 00000000 fd:00 661350 /lib64/libc-2.12.so
3b7cd86000-3b7cf86000 ---p 00186000 fd:00 661350 /lib64/libc-2.12.so
3b7cf86000-3b7cf8a000 r--p 00186000 fd:00 661350 /lib64/libc-2.12.so
3b7cf8a000-3b7cf8b000 rw-p 0018a000 fd:00 661350 /lib64/libc-2.12.so
第一个是具有可执行权限的代码段,第二个是PROT_NONE(模式---)映射(可能无法访问页面),最后两个是数据段(只读部分和读写)。
PROT_NONE的大小为MAXPAGESIZE,因此在32位和64位版本中创建的大小不同。对于32位版本,它具有4KB大小(对于i386为MAXPAGESIZE),对于64位版本为2MB(对于x86_64系统,标准MAXPAGESIZE)。
应该注意的是,这个内存实际上并没有消耗(它只占用了地址空间的地址),如下所示:
http://www.greenend.org.uk/rjk/tech/dataseg.html
“这个附加功能不会花费你任何RAM或交换空间,只是每个进程中的地址空间,这在64位平台上供应充足。其根本原因是保持库可以高效共享,但实现有点奇怪。“
只是最后一招,我发现使用pmap实用程序比检查映射文件更容易检查内存映射,并生成更简单的读取输出:
基本信息:
pmap <PID>
有关扩展信息:
pmap -x <PID>
答案 1 :(得分:1)
[不是真的答案......说出我的知识]
如果内存段真的是“私有的,不可读的,不可写的,不可执行的”那么它们就永远不会被引用,即使它们存在于VIRTUAL内存空间中,它们也永远不会占用任何实际内存,因此不用太担心。 (?)
必须是某种簿记或碎片问题。由于这些是共享库(* .so)的一部分,因此它们就是这些库的构建方式。它与您的程序无关,除了它与这些库相关联。除非您想重建这些库,或者不使用它们,否则没有太多可行的事情(无论如何都无法获得,因为它们不应该使用真正的内存)。
也许相关? 在What these memory regions for, from a Linux process?
@caf表示一些“--- p”的内存段是“保护页面”。
这表明它们的存在只是为了捕获一个迷路指针或堆栈增长到远误...在内存中有点硬分隔符,因此系统可以捕获常见错误并停止处理而不是让这些常见错误消失(其完全引用它们是一个致命的错误,它们真的永远不会使用任何真正的记忆。)
答案 2 :(得分:1)
回答为什么以及什么构成64位共享库有一块额外的内存,例如加载libc.so
并从加载器加载动态库的方式来看这个。以下是32位和64位可执行文件的strace
输出,告诉我们有mmap
&amp; mprotect
。
esunboj@L9AGC12:~/32_64bit$ strace ./crash-x86-64
...
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\200\30\2\0\0\0\0\0"...,
832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1811128, ...}) = 0
mmap(NULL, 3925208, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) =
0x7fa354f8a000
mprotect(0x7fa35513f000, 2093056, PROT_NONE) = 0
mmap(0x7fa35533e000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE,
3, 0x1b4000) = 0x7fa35533e000
mmap(0x7fa355344000, 17624, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS,
-1, 0) = 0x7fa355344000
close(3) = 0
...
esunboj@L9AGC12:~/32_64bit$ strace ./crash ... open("/lib/i386-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0000\226\1\0004\0\0\0"..., 512) = 512 fstat64(3, {st_mode=S_IFREG|0755, st_size=1730024, ...}) = 0 mmap2(NULL, 1743580, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xfffffffff7546000 mprotect(0xf76e9000, 4096, PROT_NONE) = 0 mmap2(0xf76ea000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1a3) = 0xfffffffff76ea000 mmap2(0xf76ed000, 10972, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xfffffffff76ed000 close(3) = 0 ...
密切观察strace的两件事情需要进行调查,
<强> 1。他们每个人在第一次mprotect
之后完全映射内存3次和1次呼叫{<1}}。
<强> 2。比较mmap
对64位&amp; 32位有mprotect
&amp;区域2093056B
分别受到保护。
在dl-load.c中,子例程_dl_map_object_from_fd()通过设置所需权限和零填充库的4096B
部分来动态库内存段映射到虚拟空间,并更新链接映射结构。让我们来一些代码的一部分进行更多的分析,
.bss
在上面的代码中,struct link_map *
_dl_map_object_from_fd ( )
{
...
/* Scan the program header table, collecting its load commands. */
struct loadcmd
{
ElfW(Addr) mapstart, mapend, dataend, allocend;
off_t mapoff;
int prot;
} loadcmds[l->l_phnum], *c; // l is link_map struct described for each object
of dynamic linker
size_t nloadcmds = 0;
bool has_holes = false;
...
for (ph = phdr; ph < &phdr[l->l_phnum]; ++ph)
switch (ph->p_type)
{
...
case PT_LOAD:
...
c = &loadcmds[nloadcmds++];
c->mapstart = ph->p_vaddr & ~(GLRO(dl_pagesize) - 1);
c->mapend = ((ph->p_vaddr + ph->p_filesz + GLRO(dl_pagesize) - 1)
& ~(GLRO(dl_pagesize) - 1));
...
if (nloadcmds > 1 && c[-1].mapend != c->mapstart)
has_holes = true;
...
}
...
if (has_holes)
__mprotect ((caddr_t) (l->l_addr + c->mapend),
loadcmds[nloadcmds - 1].mapstart - c->mapend, PROT_NONE);
...
}
语句中使用的l_phnum
包含ELF程序头中的条目数。理想情况下,对于每次迭代,映射每个条目段。 for
分段案例第一次点击时,它基本上是PT_LOAD
或.text
部分,其被mmapped(在strace中为.rodata
}和第二mmap
段代表PT_LOAD
部分被映射(第二个.data
在strace中)。在映射第二个mmap
段之前,会保留PT_LOAD
和mapstart
,它们指的是文本部分的开头和结尾。在下一个mapend
次迭代中,如果前一个分段PT_LOAD
不等于当前(.data)分段mapend
,则它们是两个mapstart
分段之间的一个洞(意味着{{1}之间的差距}和PT_LOAD
部分)。因此,如果它们是具有空权限的内存区域之间的漏洞,则加载程序将保护(.text
调用strace)或使其无法访问。 64位和32位进程的受保护区域 511 Vs仅 1 页面,分别为64位库增加了大量内存块。
64位无法访问区域的证据:以下.data
的Objdump为我们提供了一些虚拟地址(VA)统计信息,这些统计信息按适当方式进行了如下舍入,
mprotect
此处libc.so
(0x00000000001b5000)不等于 PT_LOAD(1) PT_LOAD(2)
mapstart VA 0x0000000000000000 0x00000000003b4000
mapend VA 0x00000000001b5000 0x00000000003A0000
(0x00000000003b4000),导致内存空洞为0x00000000001FF000(十进制PT_LOAD(1) mapend
)。
PT_LOAD(2) mapstart
在64位文本上,与32位相比,指令字节的表示更高。类似地,64位指针的大小为2093056B
,增加esunboj@L9AGC12:~/32_64bit$objdump -x -s -d -D /lib/x86_64-linux-gnu/libc.so.6
Program Header:
...
LOAD off 0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**21
filesz 0x00000000001b411c memsz 0x00000000001b411c flags r-x
LOAD off 0x00000000001b4700 vaddr 0x00000000003b4700 paddr 0x00000000003b4700 align 2**21
filesz 0x0000000000005160 memsz 0x0000000000009dd8 flags rw-
...
个字节。此外,数据结构对齐是在{64}中对齐的8B
,使得映射区域更大。
对二进制文件的简单4
命令可以显示32/64位程序内存区域之间的区别,如下所示,
8B