龙空技术网

静态链接和动态链接对比简析

micro虾米 227

前言:

眼前大家对“linux动态库的动态链接与静态链接的区别”大体比较注意,咱们都需要剖析一些“linux动态库的动态链接与静态链接的区别”的相关文章。那么小编同时在网上搜集了一些对于“linux动态库的动态链接与静态链接的区别””的相关内容,希望小伙伴们能喜欢,兄弟们快快来学习一下吧!

0. 简介

在Linux环境下进行开发工作,代码要经过编译链接生成二进制可执行文件,才能被CPU识别并执行;程序的编译过程可以参考另外一篇文章《linux程序编译过程简析》;链接过程分为两种,静态链接和动态链接;

静态链接和动态链接最明显的区别是,两者链接的时机不一样;静态链接在生成可执行文件时;动态链接在程序加载执行时;

1. 静态链接

多个目标文件经过静态链接生成静态库,静态库也可以看作是多个目标文件的集合;链接器在链接静态库时,是以目标文件为单位的,使用ar命令可以查看静态库中包含的目标文件;在引用静态库中的printf()函数时,链接器会把库中包含printf()函数实现的目标文件链接进去,如果在这个目标文件中还有很多别的函数实现,这些没有被实际使用到的函数也会被链接到结果文件中,所以应该尽量将函数实现按照功能模块分成多个目标文件,以减少将没有使用到的函数链接到可执行文件中;

静态链接过程分两步:

空间与地址的分配;扫描所有目标文件,合并相似的段,收集所有的符号信息;符号解析与重定位;调整代码位置;

静态链接的优缺点

缺点:

浪费空间,因为每个可执行程序中对多有需要的目标文件都需要一份副本,所以多个程序对同一个目标文件有依赖,同一个目标文件会在内存中存在多个副本;更新困难,因为库函数修改后,需要重新进行编译链接生成可执行程序;

优点:

在可执行程序中已经包含了执行程序需要的所有函数,在执行的时候运行速度更快;

2. 动态链接

动态链接的基本思想是把程序按照模块拆分成相对独立的部分,在程序运行时将他们链接形成完整的程序,而不是像静态链接一样,把所有程序模块链接到一起生成一个可执行文件;

动态链接在程序运行时才进行链接形成完整的程序;程序编译的链接阶段,链接器只是拷贝了一些重定位和符号信息;在程序加载时才解析so文件中代码和数据的引用;

某个程序在运行中要调用某个动态链接库中的函数时,系统会先检查所有正在运行的程序,内存中是否已有该库函数的副本,如果有,则将其共享给程序,如果没有,则链接载入需要的动态链接库文件;被调用的动态链接库函数被加载在内存的某个位置,所有调用库文件的程序都指向这个代码段,因此库文件的代码必须使用相对地址,而不是绝对地址;在编译时,这些库文件用来做动态链接库,要用地址无关代码;

2.1 动态链接过程举例

假设有两个程序demo1.o和demo2.o,都依赖库文件demolib.o,如果程序demo1.o先运行,系统先把demo1.o加载到内存中,系统检测到demo1.o中用到了库文件demolib.o中的函数,系统会加载demolib.o和其他的依赖库文件到内存中;

在程序demo2.o运行时,同样加载demo2.o到内存,之后系统检测到demo2.o也用到了demolib.o中的函数,此时由于库文件demolib.o已经加载到内存中了,此时就不需要重新加载demolib.o文件,而是将内存中已经存在的demolib.o映射到demo2.o的虚拟地址空间中进行链接,形成可执行程序;

2.2 动态链接的优缺点

优点:

1. 即使需要每个程序都依赖同一个库,该库文件不会像静态链接在内存中存在多分副本,而是多个程序执行时共享同一个库函数副本;

2. 更新方便,更新时只需替换库文件中原来的目标文件,无需将所有的程序重新编译链接;在程序运行时,新版本的目标文件会被加载到内存中并进行链接;

缺点:

由于把链接操作推迟到了程序运行时,每次程序运行都需要进行链接,性能会有一些损失;

2.3 动态链接地址重定位

动态链接把链接过程推迟到了程序运行时,在生成可执行文件时,需要用到动态链接库;即在生成可执行程序时,发现少了一个外部实现的函数,此时会检查动态链接库,如果检查到该函数名时一个动态链接符号名,可执行程序就不会对这个符号重定位,而把这个过程推迟到程序装载时进行;

3. 生成库文件

3.1 生成静态链接库

源代码文件:demo.c

链接库的源代码文件:testlib.c

$ gcc -c testlib.c -o testlib.o     // 把lib.c文件编译不链接,生成.o文件$ ar testlib.o -rc libtest.a        // 使用ar工具把.o文件打包生成.a库文件

3.2 静态链接库的使用

$ gcc demo.c -o demo -ltest -L.     // testlib.a库在链接时使用-ltest

-L:表示要链接的库所在的目录

-l:指定链接的库,编译器查找链接库时会在名称前加上lib,后边加上.a或.so来确定库名称;

3.3 生成动态链接库

源代码文件:demo.c

链接库的源代码文件:testlib.c

$ gcc -fPIC -c testlib.c -o testlib.o   // 编译不链接$ gcc -shared testlib.o -o libtest.so   // 使用-shared生成动态链接库

-fPIC:用来创建与地址无关的编译程序,为了在多个程序间共享

-shared:指定生成动态链接库

也可以将以上两步操作合并为一步

$ gcc -fPIC -shared testlib.c -o libtest.so

3.4 动态链接库的使用

$ gcc demo.c -o demo -ltest -L.     // testlib.a库在链接时使用-ltest

由于动态链接库是在程序加载执行时进行链接,所以在系统加载可执行程序时,需要库文件的名字和绝对路径,还需要系统提供的动态加载器;对于ELF格式的可执行文件,动态加载器是ld-linux.so*,它会先后搜索ELF文件中DT_RPATH段、环境变量LD_LIBRARY_PATH、/etc/ld.so.cache文件列表、/lib/,/usr/lib目录,找到链接库文件后将其加载到内存;

在程序执行时需要能够找到链接库文件:

1) 将库文件安装到/lib或/usr/lib目录下;

2) 如果库文件安装在其他目录,需要将路径添加到/etc/ld.so.cache文件中,添加步骤如下:

a) 编辑/etc/ld.so.conf文件,加入库文件所在目录的路径;

b) 运行ldconfig,重建/etc/ld.so.cache文件;

4. 总结

在网上看到有的文章中把程序和库文件的静态和动态链接方式,比作书本和笔记的关联方式;

把书本上的内容比作程序,把参考书或者黑板上的笔记比作是要链接的目标;

静态链接,就像是把笔记抄写到书本上,或者抄写到活页上,粘贴到书本上,将笔记和书本合为一个整体;这样即使忘了带参考书,或者黑板上的笔记被擦掉,都不会影响,因为笔记和书本已经融合了;

优点是,效率更高了,看书方便多了;缺点是书本越来越厚;

动态链接,就像是在书本中标记笔记的位置注释,如:该部分参考什么什么书,第几页等信息,把笔记和书本分离称互相独立的个体,将笔记单独整理称库文件;在阅读到注释的地方,需要翻看笔记时才按照注释索引,查找笔记中对应的内容;动态链接的方式,需要书本和笔记同时在手,才能正常阅读;

优点是,不影响书本的空间;缺点是,查找笔记的效率比较低;

参考资料

《程序员的自我修养----链接、装载与库》

静态链接与动态链接的区别和使用

深入浅出静态链接和动态链接

回到目录

标签: #linux动态库的动态链接与静态链接的区别