关于链接的简单总结
重温了《Expert C Programming》中第五章——对链接的思考,写点读书笔记。
1、链接器
按照分而治之的思想,编译器分成六七个部分来执行不同的功能,而这些程序是由一个叫做“编译器驱动器(compiler driver)”控制程序来调用。这几个部分按照执行的先后顺序分别是:预处理器(preprocessor)、语法语义检查器(syntactic and semantic checker)、代码生成器(code generator)、汇编程序(assembler)、优化器(optimizer)、链接器(linker)。
编译生成的目标文件是不能直接执行,它必须首先被载入到链接器中,然后链接器确认main函数为初始进入点,把符号引用绑定到内存地址,把所有目标文件集中在一起,再加上库文件,从而产生了可执行文件。
以前的UNIX系统是这样的:当链接程序时,需要使用每个库函数的一份拷贝被加入到可执行文件中。这点方式我们称之为:静态链接。但是一种更为现代和优越的方式越来越多地被我们所采用,这种方式叫做:动态链接。动态链接允许系统提供一个庞大的函数库几何,可以提供很多不同的服务。接下来具体讨论一下这两种方式的特点。
2、静态链接与动态链接的特点
我们再来回顾一下这两种方式的概念:如果函数库的一份拷贝是可执行文件的物理组成部分,那么我们称之为静态链接;如果可执行文件只是包含了文件名,让载入器在运行时能够寻找程序所需要的函数库,那么我们称之为动态链接。收集模块准备执行的三个阶段为:链接-编辑、载入和运行时链接。所以,静态链接的模块是在链接编辑并载入的,而动态链接的模块是被链接编辑后再载入,并在运行时进行链接才可以运行。这就是动态链接的妙处,即使你链接了函数库,但是你没调用到库,也不会带来额外的开销,只有当你运行了并调用了才把库函数映射到进程中来。但是即使是在静态链接中,整个库文件也并没有全部装入到可执行文件中,装入的当然只是所需要的函数而已。
动态链接的优点:
1) 根据我们上面的描述,动态链接所需要的磁盘空间比静态链接小。
2) 动态链接可以实现进程中的共享。比如说,在系统中有5个进程都同时用到一个函数库中的某一个函数,那么第一个调用该函数的进程运行之后,操作系统把该函数库相应的文本段映射到内存中,这个时候其他需要调用该函数的进程都可以共享该函数了,而不需要每次调用都重新映射,实际上就是提高了效率。换成是静态链接,5个进程需要5份拷贝映射到内存中,这就耗费了更多的物理内存,引起了更多的换页。
3) 方便更新。你可以随时更新函数库,需要调用到函数库的程序不需要重新编译,只是在运行时链接相应的函数,仅此而已。区别
3、如何创建静态库和动态库
拿经典的hello world为例子。编辑了下面一个简单的C文件:
#include <stdio.h>
void printHelloWorld(void)
{
printf("Hello, world!\n");
}
ken@Linux:~/test$ gcc -c helloworld.c
ken@Linux:~/test$ ar cr libhelloworld.a helloworld.o
#include <stdio.h>
int main()
{
printHelloWorld();
return 0;
}
ken@Linux:~/test$ gcc -o helloworld hello.c -L. -lhelloworld
接下来看看创建动态库的方法:
ken@Linux:~/test$ gcc -shared -fPCI -o libhelloworld.so helloworld.o
NED