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

How can two processes share the same Shared Library?

我一直在努力更好地了解共享库的工作原理,但我就是无法理解两件事。

1- 每个进程都有自己的虚拟内存 space 和页面 table,所以如果共享库被加载到一个进程的虚拟内存 space 那么第二个进程如何加载访问共享库,因为它不在内存中 space?

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

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

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

现在假设 a.outforking 之前也使用了 libc.so.6。您应该不难理解属于 libc.so.6 的只读页面也以完全相同的方式在进程之间共享。

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

So how can two processes have different copies of global variable,

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

对于你的第二个问题,GOT存储偏移量,你需要一个基地址来访问GOT的每个条目。请记住,我们这里所说的所有地址都是虚拟地址,因此不同的进程可以将相同的虚拟地址映射到不同的物理地址,全局变量就是这种情况。但是共享库就不是这样了,它对于所有进程都驻留在同一个物理内存地址。