linux下的静态链接库和动态链接库

静态链接和动态链接原理简介

一个程序的产生,要经过编辑器editor的编辑,编译器compiler的的编译,链接器linker的链接,然后才能够成为可以load到内存的具有执行能力的程序,由于听说过linker and loader这本书,我一直以为在操作系统中存在一个专门将程序load到内存指定位置的系统程序,这个程序叫loader,后来才发现一个程序load到内存是通过linux的系统调用execve(2)来实现,而且对于一个程序中的标明的地址都是虚地址,真正的内存使用要经过地址转换的,但这需要cpu具有内存管理单元的支持,这就把程序员重内存分配中解脱出来。

简单来讲,程序的装入主要包含以下几个步骤:

  • 读入可执行文件的头部信息以确定其文件格式及地址空间的大小;
  • 以段的形式划分地址空间;
  • 将可执行程序读入地址空间中的各个段,建立虚实地址间的映射关系;
  • 将bbs段清零;
  • 创建堆栈段;
  • 建立程序参数、环境变量等程序运行过程中所需的信息;
  • 启动运行。
想想一个程序的产生,还是背后有很多神奇的原理在支持,这里说说链接的原理,这个是个困扰我很久的问题,现在也只能看些资料然后做点总结吧。

个人觉得,静态链接的产生是由于C语言模块化的特点,为了将常用的一些功能容易归类的目标代码打包成静态链接库,这样方便管理代码,同时也方便使用,但是静态链接对内存没有节约,这在一些小系统中是很大的一个缺点。目前大部分都是采用的动态链接,动态载入的技术。

对于动态链接,动态载入:

在使用动态链接时,需要在程序映象中每个调用库函数的地方打一个桩(stub)。stub是一小段代码,用于定位已装入内存的相应的库;如果所需的库还不在内存中,stub将指出如何将该函数所在的库装入内存。

当执行到这样一个stub时,首先检查所需的函数是否已位于内存中。如果所需函数尚不在内存中,则首先需要将其装入。不论怎样,stub最终将被调用函数的地址替换掉。这样,在下次运行同一个代码段时,同样的库函数就能直接得以运行,从而省掉了动态链接的额外开销。由此,用到同一个库的所有进程在运行时使用的都是这个库的同一份拷贝。

动态链接的这一特性对于库的升级(比如错误的修正)是至关重要的。当一个库升级到一个新版本时,所有用到这个库的程序将自动使用新的版本。如果不使用动态链接技术,那么所有这些程序都需要被重新链接才能得以访问新版的库。为了避免程序意外使用到一些不兼容的新版的库,通常在程序和库中都包含各自的版本信息。内存中可能会同时存在着一个库的几个版本,但是每个程序可以通过版本信息来决定它到底应该使用哪一个。如果对库只做了微小的改动,库的版本号将保持不变;如果改动较大,则相应递增版本号。因此,如果新版库中含有与早期不兼容的改动,只有那些使用新版库进行编译的程序才会受到影响,而在新版库安装之前进行过链接的程序将继续使用以前的库。这样的系统被称作共享库系统。

动态链接技术还是很复杂的,我们只需要知道,动态链接大概的原理,实现的大概流程,这些我们都是可以通过自己的探索,可以看到写端倪的,借用linux下一些工具,比如:objdump,readelf,gdb,ldd

一般使用objdump -dx    readelf -h


静态链接库和动态链接库的创建

动态链接库的创建
gcc -fPIC -c file1.c
gcc -fPIC -c file2.c
gcc -shared libxxx.so file1.o file2.o

gcc -fPIC -shared libxxx.so file1.c file2.c

这里PIC(Position Independent Code)是地址无关代码

动态链接库的使用
gcc -L. -lxxx main.c -o main
或者
cp libxxx.so /usr/lib/
gcc main.c -o main -lxxx


静态链接库的创建
gcc -c file1.c
gcc -c file2.c
ar rs libxxx.a file1.o file2.o
注: ar命令类似于tar命令,起一个打包的作用,但是把目标文件打包成静态库只能用ar命令而不能用tar命令。选项r表示将后面的文件列表添加到文件包,如果文件包不存在就创建它,如果文件包中已有同名文件就替换成新的。s是专用于生成静态库的,表示为静态库创建索引,这个索引被链接器使用
静态链接库可以说就是一个object文件的打包,没有什么神奇的
 
静态链接库的使用
gcc libxxx.a main.c -o main
或者
cp libxxx.a /usr/lib/
gcc main.c -o main -lxxx

如果在/usr/lib/中既有libxxx.so libxxx.a这时
gcc main.c -o main -lxxx 使用的是libxxx.so
gcc -static main.c -o main -lxxx 使用的是libxxx.a
这里有个不明白的问题,如果使用
gcc -lhello main.c编译报错,gcc  main.c -lhello却可以,觉得参数放前放后不应该引起不一样的结果啊

还发现,.so文件比.a文件要大,反正看到的是这样

注:gcc常用选项

gcc -L你的路径 -lxxx(在库的目录中需要的库)



gcc -g 如果需要调试,不要忘了加上这个选项



参考文献:

程序的链接和装入及Linux下动态链接的实现

Linux C编程一站式学习第20章链接详解 宋劲彬



你可能感兴趣的:(linux下的静态链接库和动态链接库)