我一直在试图更好地了解共享库的工作方式,但是我只是无法围绕两件事说服自己。
1-每个进程都有其自己的虚拟内存空间和页表,因此,如果将共享库加载到一个进程虚拟内存空间中,那么第二个进程如何访问该共享库,因为它不在其内存空间中? >
2-我知道仅共享文本部分,而不共享全局数据,这怎么可能?我的理解是,对全局变量的每个引用都是通过GOT完成的。因此,如果我有这行代码x = glob
,那么这将大致等于汇编中的mov eax,DWORD PTR [ecx-0x10]
,其中ecx
被用作GOT的基本值。但是,如果是这种情况,那么很明显,无论哪个进程调用该行,它都将始终访问地址位于GOT中偏移量0x10的同一全局变量。那么,如果两个进程使用引用相同GOT条目的相同文本部分,那么两个进程如何具有不同的全局变量副本?
答案 0 :(得分:1)
大概您了解页表和写时复制语义。
假设您运行一个可执行文件a.out
,该文件将初始化一些全局数据,然后初始化fork()
s。您应该毫不费力地了解到a.out
的所有只读(例如代码)页现在已在两个进程之间共享(物理内存的完全相同页是mmap
放入两个虚拟内存空间。
现在假设a.out
在libc.so.6
之前也使用过fork
。您不难理解,属于libc.so.6
的只读页面也以完全相同的方式在进程之间共享。
现在假设您有两个单独的可执行文件a.out
和b.out
,都使用libc.so.6
。假设a.out首先运行。动态加载器将执行libc.so.6
到a.out
虚拟内存空间的只读映射,现在它的某些页面在物理内存中。此时,b.out
启动,动态加载程序mmap
将相同的libc.so.6
页放入其虚拟内存。由于内核已经为这些页面建立了映射,因此内核没有理由创建新的物理页面来保存映射-它可以重新使用先前映射的物理页面。最终结果与fork
二进制文件相同-在多个虚拟内存空间(和多个进程)之间共享相同的物理页面。
那么两个进程如何具有不同的全局变量副本,
非常简单:进程之间不共享读写映射(可写数据所必需的)(以便一个进程可以写入变量,并且该写操作将对其他进程不可见。