郑州400建站网站建设,建设部资质申报网站,订阅号自定义可以做链接网站不,做借贷网站平台参考动态链接 - 知乎
加上我自己的理解#xff0c;比较好懂#xff0c;但可能在细节方面有偏差,但总体是一致的
静态链接的背景
静态链接使得不同的程序开发者和部门能够相对独立的开发和测试自己的程序模块#xff0c;从某种意义上来讲大大促进了程序开发的效率#xf…参考动态链接 - 知乎
加上我自己的理解比较好懂但可能在细节方面有偏差,但总体是一致的
静态链接的背景
静态链接使得不同的程序开发者和部门能够相对独立的开发和测试自己的程序模块从某种意义上来讲大大促进了程序开发的效率原先现在程序规模也随之扩大。但静态链接的缺点也暴露出来浪费内存、磁盘空间、模块更新困难耦合性太强。 内存与磁盘空间
静态链接在计算机早期还是比较流行的但是到了后面其缺点也非常明显。比如浪费内存和磁盘空间更新模块困难等。 一个文件的转换进程 hello.c(源程序[文本])-预处理器(cpp)-hello.i(修改了的源程序[文本])-编译器(ccl)-hello.s(汇编程序[文本])-汇编器(as)-hello.o(可重定位目标程序[二进制])-链接器(ld)-hello(可执行目标程序[二进制]) 只需要注意
hello.c源文件
hello.o由hello.c编译而来
hello(可执行目标程序[二进制])最终执行文件。 比如我有a.cpp a.h,b.cpp c.cpp四个文件
a.cpp 定义了一个函数sayHello
#include iostream
void sayHello(){std::couthello world!;
}
a.h
#ifndef UNTITLED1_A_H
#define UNTITLED1_A_Hvoid sayHello();#endif //UNTITLED1_A_Hb.cpp 调用了a的函数sayHello
#include a.h
int main(){sayHello();
}
c.cpp 调用了a的函数sayHello
#include a.h
int main(){sayHello();
} 静态链接
如果运行b那么就会结合b.o,以及其调用的a.o生成可执行文件。c运行会另外调用 c.o,a.o生成可执行文件此时可执行文件可以认为长这样
#include iostream
void sayHello(){std::couthello world!;
}
int main(){sayHello();
}
此时a.o重复了两次但这两个a.o保存了相同的内容浪费内存和磁盘。 再比如Program1 Program2分别包含Program1.o和Program2.o两个模块并且还共用了Lib.o这个模块。静态链接下P1和P2都用到了Lib.o这模块所以它们同时在链接输出的可执行文件P1和P2有两个副本当同时运行两个程序Lib.o在磁盘和内存中都有两个副本浪费空间。 程序开发与发布
静态链接另一个问题是对程序的更新部署和发布也会很麻烦我如果在a中更新了sayHello那么生成的bc可执行文件里的sayHello还是老的需要重新连接很麻烦耦合度很高。
程序有任何模块更新整个程序就要重新链接发布给用户 动态链接
dll文件和so文件
dll文件和so文件都是动态链接库也叫共享对象文件 动态链接原理
动态链接库就是将程序模块相互独立的分隔开来形成独立的文件不再将它们静态地链接到一起。简单而言就是对那些组成程序目标文件的链接等到程序运行时才进行链接也就是把链接的过程推迟到运行时才进行这就是动态链接的基本思想。
动态链接没有提前链接的过程但一般文件都是动态和静态链接结合使用也有生成可执行文件这一步。 比如我编译a.cpp为共享对象文件a.so还有a.h
b.cpp为c.o此时我运行生成可执行文件生成时侦测到sayHello为动态链接的实现所以就不执行链接(如果侦测到为静态文件那么就还是老样子进行链接)
b可执行文件执行时遇到sayHello就去动态库so中找这个函数的实现位置这个找的位置有几类我们经常用的是一个环境变量叫LD_LABRARY_PATH假设这里找到了找到之后就将a.so加载到内存链接进行执行。
c执行时再用到a.so此时a.so以及被加载到了内存中所以不用再重新加载了直接链接就能使用了。 那也就说明了动态链接的其他特点
动态运行b.cpp之前必须要有a.so和a.h才能运行所需的动态库本地必须齐全。 动态链接库的存储位置以及名称
lib文件的名称要和头文件相呼应不然怎么找对应的lib呢。 比如
cudnn.h对应libcudnn.so
cudnn_adv_infer.h对应libcudnn_adv_infer.so等等
每一个头文件都要有唯一的一个对应的so文件。 一般so文件会存在lib下头文件存在include下
如cuda11.6 可以看到
再比如我们c原生的一些实现
头文件也是在include下可以看到我们的老熟人头文件iostream等等 模块
静态链接中整个程序最终只有一个可执行文件它是一个不可以分割的整体但是在动态链接下一个程序被分成了若干个文件有程序的主要部分即可执行文件Program1和 程序所依赖的共享对象Lib.so很多时候把这些部分叫做模块即动态链接下的可执行文件和共享对象都可以看做是程序的一个模块
当程序模块Program1.c被编译成Program1.o时编译器还不不知道foobar函数的地址当链接器将Program1.o链接成可执行文件时这时候链接器必须确定Program1.o中所引用的foobar函数的性质。
如果foobar是一个定义与其它静态目标模块中函数那么链接器将会按照静态链接的规则将Program1.o中的foobar地址引用重定位如果foobar是一个定义在某个动态共享对象中的函数那么链接器就会将这个符号的引用标记为一个动态链接的符号不对它进行地址重定位把这个过程留到装载时再进行程序如何找到用到的动态库以及查看用到的动态库 动态库的搜索顺序如下 LD_PRELOAD环境变量指定库路径-rpath链接时指定路径LD_LIBRARY_PATH环境变量设置路径/etc/ld.so.conf配置文件指定路径默认共享库路径/usr/liblib应该就是按顺序一个一个寻找直到找到满足要求的实现的动态库为止。
程序是如何找到动态库的 | 守望的个人博客