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

Fra*_*c M 1 linux memory-management shared-libraries dynamic-linking linux-kernel

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

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

(2) 假设启动了使用相同共享库的第二个可执行文件a.so。ld.so会识别出这个共享库已经加载到物理内存了吗?如何理解这一点?

Mar*_*lli 5

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

ld.so要识别这个共享库已经加载到物理内存了吗?如何理解这一点?

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

如果同一个文件(即具有相同路径的文件)被映射多次,内核将查看现有的映射,如果可能的话,它将重用相同的物理页以避免浪费内存。因此,理想情况下,如果共享库被加载多次,则它只能被物理分配一次。

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


(1) 实际上,内核首先创建映射,然后仅在进程尝试通过映射读取或写入物理内存时才分配物理内存。这可以防止在不需要时浪费内存。

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