为什么Linux上的动态链接可执行文件在其自己的内存空间中具有完整的libc内存空间?

时间:2014-11-20 15:50:09

标签: c++ c linux memory linux-kernel

我在64位Ubuntu系统上使用gcc -Wall -m32 test.c -o test编译了以下C代码:

#include <stdio.h>
#include <stdlib.h>

int main() {
    char * buffer;
    buffer = (char*) malloc (1048576);
    printf("hi\n");
    sleep(20);
    return 0;
}

现在,当我运行代码并执行cat /proc/PID/maps查看进程正在使用的虚拟内存范围时,我会看到以下内容:

08048000-08049000 r-xp 00000000 08:06 3805439  /home/me/test
08049000-0804a000 r--p 00000000 08:06 3805439  /home/me/test
0804a000-0804b000 rw-p 00001000 08:06 3805439  /home/me/test
f7475000-f7577000 rw-p 00000000 00:00 0
f7577000-f7720000 r-xp 00000000 08:06 8002662  /lib/i386-linux-gnu/libc-2.19.so
f7720000-f7721000 ---p 001a9000 08:06 8002662  /lib/i386-linux-gnu/libc-2.19.so
f7721000-f7723000 r--p 001a9000 08:06 8002662  /lib/i386-linux-gnu/libc-2.19.so
f7723000-f7724000 rw-p 001ab000 08:06 8002662  /lib/i386-linux-gnu/libc-2.19.so
f7724000-f7727000 rw-p 00000000 00:00 0
f7746000-f7748000 rw-p 00000000 00:00 0
f7748000-f7749000 r-xp 00000000 00:00 0        [vdso]
f7749000-f7769000 r-xp 00000000 08:06 8002671  /lib/i386-linux-gnu/ld-2.19.so
f7769000-f776a000 r--p 0001f000 08:06 8002671  /lib/i386-linux-gnu/ld-2.19.so
f776a000-f776b000 rw-p 00020000 08:06 8002671  /lib/i386-linux-gnu/ld-2.19.so
ffa60000-ffa81000 rw-p 00000000 00:00 0        [stack]

因此代码区域介于08048000和0804b000之间,那么f7475000-f7577000中缓冲区的堆上有1048576字节。但是在f7577000和f7724000之间,动态链接的libc大约有1758972字节(这几乎就是硬盘上的库的大小)。这是为什么? ld稍微低一点。

  1. 为什么系统将整个libc和ld共享对象映射到进程的内存范围?我以为只有一个指向libc的指针,它只在系统范围内加载到内存中?

  2. 此外,我绝对不需要整个1758972字节。这里发生了什么?

  3. /lib/i386-linux-gnu/libc-2.19.so只在系统内存一次?

3 个答案:

答案 0 :(得分:4)

  
      
  1. 为什么系统将整个libc和ld共享对象映射到进程中?记忆范围?我以为只有一个指向libc的指针,它只在系统范围内加载到内存中?
  2.   

它会在系统范围内映射一次,然后将这些页面映射到每个进程中。虚拟内存地址空间。这些页面由每个进程共享(至少是只读部分)

你不能只有一个&#34;指针&#34;因为指针只能引用过程中的事物&#39;自己的地址空间,所以如果一个库不在该地址空间中,你将如何取消引用指针?这也意味着需要说出这个过程&#34;好吧,我想要这个功能,就是在我的地址空间?不,但我有一个指针,所以按照&#34;哪个会复杂得多。相反,操作系统和MMU硬件执行所需的间接和映射,使其显示为每个进程的单个平面地址空间。

  
      
  1. 此外,我绝对不需要整个1758972字节。这里发生了什么?
  2.   

由于使用libc.so的每个进程都获得相同的页面,因此只需将整个事物映射一次并共享它,而不是确定每个进程需要哪些特定页面,效率要高得多。

  
      
  1. /lib/i386-linux-gnu/libc-2.19.so只在系统内存一次?
  2.   

是的,因为相同的页面被映射到每个进程。

这一切都适用于任何ELF共享库,而不仅仅是libc.so

答案 1 :(得分:2)

您必须能够访问libc.so中的元素,因此必须这样 映射到你的记忆中。您无法访问未映射的任何内容。

关于它的存在是一次还是多次,你必须解释 完全是你的意思。我将被映射到每个地址空间 使用它的过程(几乎每个过程都是如此)。但是只有 实际上在每个过程中使用的部分实际上是 加载到主内存中。 text段将直接映射 来自.so文件,交换区域中没有任何后备存储。 (一世 至少相信。我从来没有真正看过Linux,但这就是 大多数虚拟内存工作方式。这就是为什么库中的代码通常会出现的原因 必须使用-fPIC进行编译。)

答案 2 :(得分:1)

  

我以为只会有一个指向加载的libc的指针   只有一次系统内存?

这样的指针会指向什么?指针只能指向内存空间中的某些内容。

但实际上 只在系统范围内存在于内存中。通过内存管理的神奇之处,它可以出现在多个进程的内存中,但实际上只能在内存中运行一次。

仅映射实际使用的libc部分在理论上似乎是可能的,但可能不值得复杂,因为在64位计算机上没有任何好处。它将涉及处理共享库的机制的重要补充。