操作系统是怎么实现动态链接库被多个进程共享的

操作系统是如何实现动态链接库被多个进程共享的
第一个程序A,链接了共享库比如 la.so,这时A的可执行文件和共享库 la.so都会被读进内存;如果第二个程序B也链接了 la.so,这时B的可执行文件被读入内存,系统是怎么知道 la.so已经在内存中了呢(是否在内核或其他地方具有某种记录)?
另外系统共享库的数据部分由进程独享,而代码部分可以多个进程共享,理论上这时只要把进程B的虚拟地址空间映射到 la.so的指令部分存放的物理内存就可以了,系统是怎么记录 la.so 的物理内存地址呢? 这个过程是由加载器还是动态链接器或者其他的成分来完成呢?
------解决思路----------------------
引用:
Quote: 引用:

《深入解析Windows操作系统-Windows Internals》?

赵四老师,向您请教一个问题。我看《程序员的自我修养》中关于动态链接的章节提到,对于定义在共享模块的全局变量(例如gloabl),如果被主模块(即可执行程序)引用的话,“程序的主模块的代码不是地址无关代码”,“它引用这个全局变量的方式跟普通的数据访问的方式一样,编译器会产生这样的代码”,movl $0x01 xxxxxxxx。xxxxxxxx就是全局变量的地址。“由于可执行文件在运行时并不进行代码重定位,所以变量的地址必须在链接过程中确定下来”,因此,链接器会在.bss段创建一个全局变量的副本。解决办法是,共享库内部定义的变量,当做主模块定义的变量处理,即在GOT中间接跳转,将GOT中的全局变量的地址指向主模块。
这段话有些不理解。什么叫做“可执行文件在运行时并不进行代码重定位”?如果在主模块中调用共享库的函数,可执行文件不进行重定位,怎么知道函数的地址呢?我认为可执行文件也是通过GOT在加载时重定位调用外部模块的函数,而且这种做法对于上述问题也是可行的。不知道您怎么理解这段话?

重定位是由于程序运行时被加载到的地址和编译链接时指定的地址不同时才可能会发生的,由于可执行程序通常是第一个被加载的模块,加载的时候整个虚拟内存空间几乎都是空的,所以一定可以把它加载到链接的时候指定的那个地址上,因此也就不需要重定位,既然不需要重定位就没有必要把它编译成位置无关的代码了。
至于说程序中调用其它动态模块的函数,那是另外一个故事了,在 windows 中这个是通过导入表来完成的,在 linux 中有动态链接机制,他们都是在加载的时候通过符号查找来确定地址的,并不是通过重定位...         重定位只是解决模块内部引用自己的全局变量导致的问题。