6

我一直在尝试更好地了解共享库的工作原理,但我无法解决两件事。

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

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

4

2 回答 2

7

大概您了解页表和写时复制语义。

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

现在假设在ing之前a.out也使用libc.so.6过。fork您应该可以毫不费力地理解,属于的只读页面libc.so.6也以完全相同的方式在进程之间共享。

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

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

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

于 2018-10-31T02:42:06.720 回答
0

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

于 2021-09-22T08:28:22.333 回答