分类: LINUX
2009-04-13 15:18:54
一、引言 通常情况下,对函数库的链接是放在编译期间(compile time)完成的。所有相关的对象文件(object file)与牵涉到的函数库(library)被链接并合成一个可执行文件(executable file)。程序在运行时,与函数库无关,所有需要的函数已拷贝到可执行文件中。所以这些函数库被成为静态库(static libaray),文件名为“libxxx.a”的形式。 其实,我们也可以把库函数的链接载入推迟到程序运行期间(runtime)。这也就是所谓的动态链接库(dynamic link library)技术。 二、动态链接库的特点与优势 首先让我们来看一下,把库函数推迟到程序运行期间载入的好处: 1. 可以实现进程之间的资源共享 就是说,某个程序在运行中要调用某个动态链接库函数的时候,操作系统首先会查看所有正在运行的程序,看在内存里是否已有此库函数的拷贝。如果有,则让其共 享那个已经在内存中存在的拷贝;否则需要链接载入。这样的模式虽然会带来一些“动态链接”额外的开销,却大大的节省了系统的内存资源。C的标准库就是动态 链接库,也就是说系统中所有运行的程序共享着同一个C标准库的代码段。 2. 将一些程序升级变得简单。用户只需要升级动态链接库,而无需重新编译链接其他原有的代码就可以完成整个程序的升级。Windows 就是一个很好的例子。 3. 甚至可以真正做到链接载入完全由程序员在程序代码中控制。 程序员在编写程序的时候,可以明确的指明什么时候或者什么情况下,链接载入哪个动态链接库函数。你可以有一个相当大的软件,但每次运行的时候,由于不同的操作需求,只有一小部分程序被载入内存。所有的函数本着“有需求才调入”的原则,于是大大节省了系统资源。 三、动态链接库的创建 编译参数解析 最主要的是GCC命令行的选项-shared,该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号),不用该标志外部程序无法连接,相当于一个可执行文件。 -fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。 -Lpath:表示要连接的库在此目录中。 -lxxx:编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来确定库的名称。 LD_LIBRARY_PATH:这个环境变量指示动态连接器可以装载动态库的路径。 当然如果有root权限的话,可以修改/etc/ld.so.conf文件,然后调用/sbin/ldconfig来达到同样的目的,不过如果没有root权限,那么只能采用输出LD_LIBRARY_PATH的方法了。 由于动态链接库的共享特性,它们不会被拷贝到可执行文件中。在编译的时候,编译器只会做一些函数名之类的检查。在程序运行的时候,被调用的动态链接库函数 被安置在内存的某个地方,所有调用它的程序将指向这个代码段。因此,这些代码必须使用相对地址,而不是绝对地址。在编译的时候,我们需要告诉编译器,这些 对象文件是用来做动态链接库的,所以要用地址不无关代码(Position Independent Code)。 对gcc编译器,只需添加上-fPIC标签,如: gcc -fPIC -c file1.c gcc -fPIC -c file2.c gcc -shared libxxx.so file1.o file2.o 注意到最后一行,-shared标签告诉编译器这是要建立动态链接库。这与静态链接库的建立很不一样,后者用的是ar命令。也注意到,动态链接库的名字形式为“libxxx.so”后缀名为“.so”。 四、动态链接库的使用 使用动态链接库,首先需要在编译期间让编译器检查一些语法与定义。 这与静态库的使用基本一样,用的是-Lpath和-lxxx标签。如: gcc file1.o file2.o -Lpath -lxxx -o program.exe 编译器会先在path目录下搜索libxxx.so文件,如果没有找到,则搜索libxxx.a(静态库)。 在程序运行期间,也需要告诉系统去哪里找你的动态链接库文件。在UNIX下是通过定义名为 LD_LIBRARY_PATH 的环境变量来实现的。只需将path赋值给此变量即可。 csh 命令为: setenv LD_LIBRARY_PATH path_to_dll 一切安排妥当后,你可以用ldd命令检查是否连接正常。 ldd program.exe 如果一切顺利,它将会输出你所用到的函数库的清单。否则,将会抱怨无法找到某个库。 |