静态库(archive) .a文件
动态库(shared object) .so文件
编译器包括预处理器(preprocessor)、语法和语义检查器(synatatic and semantic checker)、代码生成器(code generator)、
汇编程序(assembler)、优化器(optimizer)、链接器(linker)
如果函数库的一份拷贝是可执行文件的物理组成部分,那么这就是静态链接;
如果可执行文件只是包含了文件名,让载入器在运行时能够寻找程序需要的函数库,那么我们称之为动态链接。
收集模块准备执行的三个阶段的规范名称是链接、编辑(link-editing)、载入(loading)、运行时链接(runtime linking)
静态链接的模块被链接编辑并且被载入以便运行。
动态链接的模块被链接编辑后载入,并在运行时进行链接以便运行。程序执行时,
在main()函数被调用前,运行时载入器把共享的数据对象载入到进程的地址空间。外部函数被真正调用之前,运行时载入器并不解析它们。
所以即使链接了函数库,如果没有实际调用,也不会带来额外开销。
动态链接的优点:
可执行文件的体积非常小
可以更加有效的利用磁盘空间
动态链接通过以下2个方面提高性能:
1、动态链接可执行文件比功能相同的静态链接可执行文件体积小。它能够节省磁盘空间和虚拟内存,
因为函数库只有在需要时才映射到内存中。以前,避免把函数库的拷贝绑定到每个可执行文件的唯一方法就是把服务
置于内核中而不是函数库中,这就带来可怕的“内核膨胀”。
2、所有动态链接到某个特定函数库的可执行文件在运行时共享该函数库的一个单独拷贝。操作系统内核保证映射到内存中
的函数库可以被所有使用它们的内存共享。这就提供更好的I/O和交换空间利用率。
函数库被多次链接的时候,静态链接将占用更多的物理内存。
动态链接使得函数库的版本升级更为容易。新的函数库可以随时发布,只要安装在系统中,旧的程序就能够自动获得新版本函数库的优点,
而无需从新链接。
动态链接允许用户用户在运行时选择需要的函数库。用户可以根据自己的喜好,在程序执行用一个库文件取代取代另一个库文件
动态链接是一种just-in-time的链接,这意味着程序在运行时必须要找到它所需要的函数库。链接器把库文件名或者文件路径植入可执行文件
来完成这一点。这意味着函数库的路径不能随意移动。
-L/home/linden -R/home/linden -L和-R分别告诉链接器在链接时和运行时从哪个目录寻找需要的函数库。
·与提取动态库的符号相比,静态库中的符号提取的方法限制更严
archive(静态库)和shared object(动态库)的动作不同。
在动态链接中,所有的库符号进入输出文件的虚拟地址空间中,所有的符号对于链接在一起的所有文件都是可见的。
相反,对于静态链接,在处理archive时,它只是在archive时,它只是在archive中查找载入器当时所知道的未定义符号。
简而言之,在编译器命令行中各个静态链接库的出现顺序是非常重要的。链接器会被“函数库是在哪里找到的”,“它是以什么次序出现”
之类的问题,搞得手忙脚乱,以为符号是从左到右进行解析的。如果是相同的符号在不同函数库中,将会有不同的解释。
同名库的链接优先问题
同一个库如果同时存在动态库和静态库,优先链接动态库,除非使用--static强制使用静态库。
1、 a.out assembler output(汇编程序输出)的缩写。
这是由于历史原因,之前是所有的源文件连接在一起,然后进行汇编,而汇编的输出保存在a.out中
实际上a.out是由链接器输出。
缺省使用a.out这个名字是UNIX“没什么理由,但我们就是这样做的”思维的一种体现
2、目标文件和可执行文件可以有几种不同的格式。但是所有不同格式都是段(segments)。
就目标文件而言,它们是二进制文件中简单的区域,里面保存中和某种特定类型相关的所有信息。
section是ELF文件的最小组织单位(ELF:Extension Linker Format,可扩展链接器格式)
一个段(segment)一般包含几个section。
!!不要跟InterX86中段的概念混淆。!!
在UNIX中,段表示一个二进制文件相关的内容块。
当在一个可执行文件中运行size命令时,它会告诉你这个文件中的三个段(文本段、数据段、bss段)
%echo; echo "text data bss total" ;size a.out
text data bss total
1548 4236 4004 = 9788
为何a.out要以段的形式组织呢?
段可以方便地映射到链接器在运行时可以直接载入的对象。
载入器只是去文件中每个段的映像,并直接将它们放入内存中。
段在正在执行的程序中(即进程)中是一块内存区域。每个区域都有特定的目的。
文本段包含程序指令,直接拷贝到内存中运行。(该段属性为 read-and-execute only)
数据段包含经过初始化的全局和静态变量以及它们的值。
BSS段的大小从可执行文件中得到,然后链接器得到这个大小的内存块。
堆栈段,用于保存局部变量、临时数据、传递到函数中的参数等。
同时,堆空间(heap)用于动态内存的分配。
“C语言运行时的函数非常少,且个个短小精悍。相反的例子是C++。”
跟踪调用链是C语言提供的服务之一。
哪些函数调用了哪些函数?当下一个return语句执行后,控制将返回何处?
怎么解决上述的2个问题呢?经典机制:堆栈中的过程活动记录
过程活动记录:一种数据结构,用于支持过程调用,并记录调用结束后返回调用点所需要的全部信息。
局部变量(local varibales) |
参数(argument) |
静态链接(static link) |
指向先前结构的指针 |
返回地址(return address) |
对堆栈怎么实现函数的调用的描述也同时解释了为什么不能从函数返回一个指向该函数局部变量的指针。
char * favorite_fruit()
{
char deciduous[] = "apple";
return deciduous;
}
成为一个悬垂指针(danging pointer)。如果想返回一个指向在函数内部定义的变量的指针时,要把那个变量声明为static。这样就能保证变量保存在数据段中而不是堆栈中。
·auto在实际中用不到。(但是在C++11中, auto表示自动匹配类型,是一种重要应用)