两个进程如何共享同一个共享库?

Gam*_*anA 6 unix shared-libraries dynamic-linking position-independent-code got

我一直在努力更好地了解共享库的工作原理,但我无法围绕两件事进行思考。

1- 每个进程都有自己的虚拟内存空间和页表,因此如果共享库被加载到一个进程虚拟内存空间中,那么第二个进程如何访问该共享库,因为它不在其内存空间中?

2- 我知道只有文本部分是共享的,而全局数据不是,这怎么可能?我的理解是,对全局变量的每次引用都是通过全局偏移表(简称 GOT)完成的。所以,如果我有这行代码,x = glob那么这将大致等于mov eax,DWORD PTR [ecx-0x10]汇编中的东西,其中ecx用作 GOT 的基值。但如果是这种情况,那么很明显,无论哪个进程调用该行,它将始终访问相同的全局变量,其地址位于 GOT 中的偏移量 0x10。那么,如果两个进程使用引用相同 GOT 条目的相同文本部分,那么它们如何拥有不同的全局变量副本呢?

Emp*_*ian 7

想必您了解页表和写时复制语义。

假设您运行一个可执行文件a.out,它初始化一些全局数据,然后fork()是 s。您应该很容易理解 的所有只读(例如代码)页面a.out现在在两个进程之间共享(完全相同的物理内存页面被mmap编入两个虚拟内存空间)。

现在假设在ing之前a.out也使用libc.so.6fork。您应该很容易理解属于的只读页面libc.so.6也在进程之间以完全相同的方式共享。

现在假设您有两个单独的可执行文件,a.out并且b.out都使用libc.so.6. 假设 a.out 首先运行。动态加载器将执行libc.so.6a.out虚拟内存空间的只读映射,现在它的一些页面在物理内存中。在那一点上,b.out启动,并且动态加载器mmap将相同的libc.so.6页面放入其虚拟内存中。由于内核已经有这些页面的映射,内核没有理由创建新的物理页面来保存映射——它可以重新使用以前映射的物理页面。最终结果与forked 二进制文件相同——相同的物理页面在多个虚拟内存空间(和多个进程)之间共享

那么两个进程如何拥有不同的全局变量副本,

非常简单:读写映射(可写数据所需)在进程之间共享(因此一个进程可以写入变量,而该写入对另一个进程不可见)。