Linux共享库加载和与其他进程共享代码。

问题描述 投票:0回答:1

假设我有一个共享图书馆 a.so 这是我的可执行文件第一次加载的。我的理解是,在VMA的中间,共享库的文本部分是被映射的。我有两个问题。

(1)ld.so是否要把这个共享内存文本部分的页面加载到物理内存中,然后映射到该进程的VMA中?

(2)假设启动第二个可执行文件,它使用的是同一个共享库。a.so. ld.so要识别这个共享库已经加载到物理内存了吗?怎么理解呢?

linux memory-management linux-kernel shared-libraries dynamic-linking
1个回答
3
投票

准确地说,它不是 ld.so的工作是保留物理内存或管理或选择虚拟和物理内存之间的映射,这是内核的工作。当 ld.so 加载一个共享库,它是通过 mmap syscall,然后内核分配所需的物理内存(1) 并在库文件和物理内存之间创建一个虚拟映射。然后由 mmap 是映射库的虚拟基地址,然后动态加载器将把它作为基地址来服务于对该库函数的调用。

ld.so 要识别这个共享库已经加载到物理内存了?怎么理解呢?

它不是 ld.so,但内核要识别这个问题。这是一个复杂的过程,但为了简单起见,内核会跟踪哪个文件被映射到哪里,并且可以检测到何时有请求再次映射已经映射的文件,尽可能避免物理内存分配。

如果同一个文件(即路径相同的文件)被映射多次,内核会查看已有的映射,并进行 如有可能 它将重复使用相同的物理页面,以避免浪费内存。所以在理想情况下,如果一个共享库被多次加载,可以只进行一次物理分配。

但实际上并不是这么简单。由于内存也是可以写入的,所以这种物理页面的 "共享 "显然只有在需要共享的页面与文件的原始内容不变的情况下才能发生(否则不同的进程映射同一个文件或库会相互干扰)。对于代码部分,基本都是如此(.text),因为它们通常是只读的,对于其他类似的部分(如只读数据)也是如此。如果RW部分没有被修改,也可能发生这种情况。(2). 因此,简而言之。.text 已经加载的库的段通常只分配到物理内存一次。


(1)实际上,内核会先创建映射,然后只有当进程试图通过映射对其进行读写时才会分配物理内存。这样可以避免在不需要的时候浪费内存。

(2)这种共享物理内存的技术是通过一个 抄写 机制,内核初始映射 "干净 "的页面,并在写入时将其标记为 "脏",根据需要复制它们。


0
投票

Linux共享库通常是位置独立的(可执行文件也是如此,因为地址空间随机化)。位置独立的代码在运行时使用GOT和PLT重定位机制来定位所有具有外部链接的符号。

当动态链接器 ld.so 加载一个独立于位置的可执行文件或共享库,只有GOT和PLT段必须在进程内存中修改。.text 段与代码被映射到进程内存中作为只读。当另一个进程加载相同的可执行文件或共享库时,它最终会将相同的页面框架映射到以下地方。.text 段已经被其他进程加载了,但GOT和PLT却没有加载,而GOT和PLT是特定于一个进程地址空间布局的。

ld.so 基本上做到了 mmap(NULL, text_segment_len, PROT_EXEC | PROT_READ, MAP_SHARED, elf_file_fd, text_segment_offset) 当加载ELF文件时。这将填充内核的 页面缓存 (在第一次访问时),这样另一个进行此调用的进程就会映射已经存在于页面缓存中的页面框架。

请看 与位置无关的代码 更多信息。

© www.soinside.com 2019 - 2024. All rights reserved.