基本修养实战篇(八) 动态链接序篇

前面讲了静态链接有一些优化手段可以减少生成目标的体积。但是不同的目标文件中还是始终都会存在同一个静态库文件。这就是比较麻烦的事情了,静态链接至此也无能为力了。另外,如果程序是依赖静态库的,那如果日后静态库一旦更新,整个程序就要重新编译发布。想像一下,王者荣耀那么大的程序,如果每次修复了某个简单的bug, 但是却要你一次动辄下载更新几个G的文件,用户是断然不同意的。这个时候,动态链接的技术就登场了。

动态链接,本质上是把链接器的工作,揽过来由操作系统来做。也就是说,对于现代操作系统,支持动态链接时它的一个基本功能和特性,我们所希望的一个库多个进程共享,这种事情,想想就知道得操作系统来做。

其工作原理是这样的,首先是模块不再被编译成静态库,而是通过 -shared 和 -fPIC选项,告诉编译器我们要编一个动态库。链接器在链接时,发现我们的主程序引用了一个符号,这个符号定义在动态共享对象中(Dynamic Shared Object), 这样链接器就会把它标记成一个动态链接的符号,不对它进行地址重定位,把这个工作留到装载的时候由操作系统来完成。由此可见,这里涉及到了编译器,链接器和操作系统的合作。


 

动态链接程序运行时的地址空间分布:

 

 这个时候,有一个问题浮现了,共享对象在被装载时,如何确定它在进程虚拟地址空间中的位置?

这个时候就需要装载时重定位了。静态链接时的重定位叫做链接时重定位,这里的是装载时重定位。

但是还有一个比较严重的问题,那就是动态链接模块被装载到进程的虚拟空间中后,指令部分是要在多个进程之间共享的(节约空间占用),但是呢装载时重定位如果直接修改指令中重定位的部分,会导致别的进程没办法使用。又或者,一个进程修改了so的某个static的变量,那别的进程也要跟着受到牵连。我们之前做过一个实验,两个程序都去改一个so的某个static变量,发现互不影响,那么这是怎么做到的呢?

这里就要引入一个新概念了,叫做地址无关码。其核心思想是指令部分不变,数据部分,每个进程一个副本。

然后,在一个进程的虚拟地址空间中,我们来看一下其典型的内存布局。

 

 可以看到这个地方用的是映射,不会把系统装载过的库再装载一遍。

 

posted on 2021-10-22 08:53  疾速瓜牛  阅读(64)  评论(0编辑  收藏  举报

导航