共享库是否使用与应用程序相同的堆?

时间:2012-01-15 01:06:05

标签: c linux gcc x86-64

假设我在Linux中有一个使用共享库(.so文件)的应用程序。我的问题是这些库中的代码是否会在与主应用程序相同的堆中分配内存,还是使用自己的堆?

例如,.so文件中的某些函数调用malloc,它是否会使用与应用程序相同的堆管理器或另一个?此外,那些共享内存中的全局数据如何呢?它在哪里?我知道应用程序它位于bss和数据段中,但不知道它们对于那些共享对象文件的位置。

2 个答案:

答案 0 :(得分:27)

  

我的问题是这些库中的代码是否会在与主应用程序相同的堆中分配内存,还是使用自己的堆?

如果库使用与应用程序相同的malloc/free(例如来自glibc) - 那么是的,程序和所有库将使用单个堆。

如果库直接使用mmap,它可以分配内存,而不是程序本身使用的内存。

  

例如,.so文件中的某些函数调用malloc,它是否会使用与应用程序相同的堆管理器或另一个?

如果.so中的函数调用malloc,则此malloc与从program调用的malloc相同。您可以在

中看到Linux / glibc(> 2.1)中的符号绑定日志
 LD_DEBUG=bindings ./your_program

是的,堆管理器的几个实例(具有默认配置)不能在不了解彼此的情况下共存(问题在于保持brk分配的堆大小在实例之间同步)。但是,当多个实例可以共存时,可以进行配置。

大多数经典的malloc实现(ptmalloc *,dlmalloc等)可以使用两种从系统获取内存的方法:brkmmap。 Brk是经典的堆,它是线性的,可以增长或缩小。 Mmap允许在任何地方获得大量内存;并且您可以按任何顺序将此内存返回给系统(释放它)。

当构建malloc时,可以禁用brk方法。然后malloc将仅使用mmap来模拟线性堆,甚至将禁用经典线性堆,并且所有分配都将来自不连续的mmaped fragmens。

因此,某些库可以拥有自己的内存管理器,例如malloc编译时禁用brk或使用非malloc内存管理器编译。此管理器应具有mallocfree以外的函数名称,例如malloc1free1,或者不应将此名称显示/导出到动态链接器。

  

此外,那些共享内存中的全局数据如何呢?它在哪里?我知道应用程序它位于bss和数据段中,但不知道它们对于那些共享对象文件的位置。

您应该将程序和.so视为ELF文件。每个ELF文件都有“程序头”(readelf -l elf_file)。数据从ELF加载到内存的方式取决于程序头的类型。如果类型为“LOAD”,则文件的相应部分将私有mmap ed(Sic!)到内存。通常,有2个LOAD段;第一个用于带有R + X(读取+执行)标志的代码,第二个用于带有R + W(读取+写入)标志的数据。 .bss.data(全局数据)部分都放在LOAD类型的段中,并具有写入启用标志。

可执行文件库和共享库都有LOAD段。一些段具有memory_size>文件大小。这意味着片段将在内存中扩展;它的第一部分将填充ELF文件中的数据,而大小的第二部分(memory_size-file_size)将使用*bss和{{1}填充零(对于mmap(/dev/zero)部分) }

当内核或动态链接器将ELF文件加载到内存中时,他们不会考虑共享。例如,您想要两次启动相同的程序。第一个进程将使用mmap加载ELF文件的只读部分;第二个进程将执行相同的mmap(如果aslr处于活动状态 - 第二个mmap将进入不同的虚拟地址)。页面缓存(VFS子系统)的任务是将数据的单个副本保存在物理内存中(使用COPY-on-WRITE也称为COW);而mmap只会将每个进程中虚拟地址的映射设置为单个物理位置。如果任何进程将更改内存页面;它将被写入唯一的私有物理内存时被复制。

对于内核的ELF加载器(memset(0)glibc/elf/dl-load.c),ld.so和_dl_map_object_from_fd的加载代码位于linux-kernel/fs/binfmt_elf.celf_map)。搜索load_elf_binary

因此,全局数据和bss数据在每个进程中始终是私有mmaped,并且它们受COW保护。

堆栈和堆栈在运行时使用brk + mmap(堆)和OS内核在类似brk的进程中自动分配(用于主线程堆栈)。其他线程的堆栈在PT_LOAD中分配mmap

答案 1 :(得分:9)

符号表在Linux中的整个过程中共享。 malloc()对于该过程的任何部分都与所有其他部分相同。所以,是的,如果进程的所有部分都通过malloc()等访问堆,那么它们将共享相同的堆。