早期的程序,链接是手动完成的,而现代系统中,链接是由链接器完成的;
一个程序经过预处理,编译,汇编,链接而完成的;详情请看该文http://blog.csdn.net/sykpour/article/details/25550663;
本文主要侧重在链接环节;
目标文件分为三种,
(1)上述过程中汇编后的.o就是可重定位目标文件,包含二进制数据和代码;
(2)可执行目标文件就是链接完成的,其形式是可以被直接拷贝到存储器并执行;
(3)共享目标文件,是在加载或运行事被动态加载到存储器并链接;
注意:在C语言中,任何带有static修饰的,无论是函数还是变量都是私有的;
程序测试代码
add.c
#include
int add(int x, int y)
{
printf("%s\n", "in add");
return x + y;
}
#include
int sub(int x, int y)
{
printf("%s\n", "in sub");
return x - y;
}
main.c
#include
int main(void)
{
int x = sub(1, 2);
printf("sub:%d\n", x);
x = add(1, 2);
printf("sub:%d\n", x);
return 1;
}
静态链接的任务是符号解析(将每一个符号刚好与一个符号定义联系起来),重定位(就是将每一个符号定义与存储器位置联系起来)
静态链接
gcc -o test1 main.c sub.c add.c #(对sub.c add.c是静态链接,而对于printf.o并不是静态链接)
./test #外壳调用加载器
静态库
(1)静态库可以解决大量相关函数对应用程序可用的问题,但是需要它也需要更新,也就会导致所有的应用程序,都需要重新链接;而且,如果10个应用程序都需要用到printf.o,那么在存储器中会有10份printf.o,显然是一种浪费;
(2)当使用-static选项时,就是告诉编译器程序,链接器需要构造一个完全链接的可执行目标文件(构造时,只取需要用到的可重定位文件,如printf.o),它可以直接加载到存储器并运行,在加载时是无需再次链接的,但是文件比较大;
(3)
gcc -c sub.c add.c
ar rcs lib2.a sub.o add.o
gcc -o test2 main.c lib2.a #(对sub.c add.c是静态链接,而对于printf.o并不是静态链接)
gcc -static -o test3 main.c lib2.a #(对sub.c add.c是静态链接,而对于printf.o也是静态链接)
动态链接共享库
(1)动态链接共享库,可以在运行时加载到任意的存储器地址(注意是在应用程序被加载时,动态链接器加载和链接过共享库的),并和一个在存储器的程序链接起来;微软OS中是以.dll后缀表示,而类Unix系统中是.so后缀表示;
(2)共享库的共享方式针对上述的10个可执行目标文件,只有一份,并不是像静态库嵌入的;针对存储器,一个共享库的.text段也是被不同的正在运行的进程共享的;
(3)gcc -shared -fPIC -o libshared.so add.c sub.c #其中-fPIC表示编译器生成与位置无关的代码(PIC可以使的链接器不修改代码就可以在任何地址加载和执行这些代码,否则要指明地址空间片), -shared表示创建一个共享的目标文件;
(对sub.c add.c,printf.o都是动态链接)
gcc -o test4 main.c ./libshared.so
运行时加载和运行任意共享库(用时加载);
上述的可执行目标文件大小对比如下:显而易见,test3的大小很大;