编译器的作用
通常编译器被氛围六七个小的程序:
- C预处理器,得到main.i文件
- 前端做语法语义分析,然后后端生成汇编的指令代码main.s文件
- 优化器可以放在任何一个地方
- 汇编程序会将汇编文件进行处理直接通过ISA指令将汇编代码,生成可重定位的二进制文件main.o,注意这里的二进制文件还不可执行,他的各个段对应汇编文件的各个段。
- 然后就到了链接器,他首先载入main函数,然后把符号引用绑定到内存地址,把所有目标文件集中在一起,再加上库文件,最后产生可执行的二进制文件。
动态链接和静态链接
如果函数库的一份副本是可执行文件的物理组成部分,我们称之为静态链接。
如果可执行文件只包含了文件名,让载入器在运行时才寻找所需要的函数,我们称之为动态链接。库在运行时才被映射到进程内存中。
上图比较我们可以看出静态链接和动态链接生成的可执行文件差距非常大。
并且即使是静态链接实际上也并不是将整个库都装入到可执行文件中,而只是将所需要用到的函数装入到可执行文件。
动态链接
一种更为现代和优越的被称作动态链接的方法逐渐被采用。
程序将在运行时去寻找函数库集合中的函数执行,而不是将他们作为自身可执行文件的一部分。
动态链接时一种JIT链接 just in time。
可执行文件必须能找到指定的动态链接路径,函数库不能随便移动,因为可执行文件需要定位到函数库路径。
动态链接库由链接器ld创建。文件扩展名为.so。表示shared object. 每一个链接到该函数库的程序都共享他的一份副本
动态链接的优点
- 可执行文件体积可以非常小。
- 链接阶段时间也会缩短,因为有些工作被推迟到了载入时。
- 将程序和特定的函数库版本进行解耦,使得函数库升级更容易;
- 所有可执行文件在运行时共享动态链接函数库的单独副本,更节省内存空间。比如8个基于XView的函数库的程序在运行,只需要把一个XView函数库文本段映射到内存中,其余7个都将共享这个副本。
函数库链接的几个小秘密
- 动态库文件.so,静态库文件.a。动态库文件命名规则为libname.so或者后面加上版本号libname.so.1比如线程库函数libthread.so
- 通过-lthread选项,告诉编译时链接到某个函数库的名字。比如我要链接libthread.so,那么编译器是如何知道的呢。这时我们需要设定编译选项-lthread,他省略了lib 和.so后缀,然后前面加个-l
- 编译器如何确定函数库路径。首先他会查找一些特殊位置/usr/lib中查找函数库。编译选项-Lpathname和-Rpathname来告诉链接器一些其他查找路径的目录。也可以在运行程序时设定环境变量LD_LIBRARY_PATH和LD_RUN_PATH来提供这类信息。但是设定环境变量的方法已经不被提倡了。
- 如何确定必须链接哪些库。那就是观察文件包含的头文件 ,需要使用 哪些库。
- ldd命令:使用ldd + 可执行文件 命令来显示程序所需要的函数库。