分析Linux下静态链接库和动态链接…

来源:互联网 发布:linux 修改时区 编辑:程序博客网 时间:2024/06/10 05:53
注明:资料部分来源于网络
 
代码编译运行过程:
源码.c->(预处理)->预处理过的.i源文件->(编译)->汇编文件.S->(汇编)->目标文件.o->(链接)->elf可执行程序

一、静态链接库与动态链接库
静态链接库
通常情况下,对函数库的链接是放在编译时期compiletime)完成的。所有相关的对象文件(objectfile)与牵涉到的函数库(library)被链接合成一个可执行文件executablefile)。程序在运行时,与函数库再无瓜葛,因为所有需要的函数已拷贝到自己门下。所以这些函数库被成为静态库(staticlibaray),通常文件名为“libxxx.a”的形式。
其实,我们也可以把对一些库函数的链接载入推迟到程序运行时期runtime)。这就是如雷贯耳的动态链接库(dynamiclink library)技术。动态链接库的名字形式为 “libxxx.so” 后缀名为 “.so”

动态链接库
动态链接是相对于静态链接而言的。所谓静态链接是指把要调用的函数或者过程链接到可执行文件中,成为可执行文件的一部分,换句话说,函数和过程的代码就在程序的exe文件中,该文件包含了运行时所需的全部代码,当多个程序都调用相同函数时,内存中就会存在这个函数的多个拷贝,这样就浪费了宝贵的内存资源。而动态链接所调用的函数代码并没有被拷贝到应用程序的可执行文件中去,而是仅仅在其中加入了所调用函数的描述信息(往往是一些重定位信息),仅当应用程序被装入内存开始运行时,在Windows的管理下,才在应用程序与相应的DLL之间建立链接关系。当要执行所调用DLL中的函数时,根据链接产生的重定位信息,Windows才转去执行DLL中相应的函数代码。一般情况下,如果一个应用程序使用了动态链接库,Win32系统保证内存中只有DLL的一份复制品。

二、静态链接库、动态链接库各自的特点

a.动态链接库有利于进程间资源共享

当某个程序的在运行中要调用某个动态链接库函数的时候,操作系统首先会查看所有正在运行的程序,看在内存里是否已有此库函数的拷贝了。如果有,则让其共享那一个拷贝;只有没有时才链接载入。这样的模式虽然会带来一些“动态链接”额外的开销,却大大的节省了系统的内存资源。C的标准库就是动态链接库,也就是说系统中所有运行的程序共享着同一个C标准库的代码段。而静态链接库则不同,如果系统中多个程序都要调用某个静态链接库函数时,则每个程序都要将这个库函数拷贝到自己的代码段,显然将占有更大的内存资源。

b. 将一些程序升级变得简单

用静态库,如果库发生变化,使用库的程序要重新编译。使用动态库,只要动态库提供给该程序的接口没变,只要重新用新生成的动态库替换原来就可以了。

c. 甚至可以真正做到链接载入完全由程序员在程序代码中控制。
程序员在编写程序的时候,可以明确的指明什么时候或者什么情况下,链接载入哪个动态链接库函数。你可以有一个相当大的软件,但每次运行的时候,由于不同的操作需求,只有一小部分程序被载入内存。所有的函数本着有需求才调入的原则,于是大大节省了系统资源。比如现在的软件通常都能打开若干种不同类型的文件,这些读写操作通常都用动态链接库来实现。在一次运行当中,一般只有一种类型的文件将会被打开。所以直到程序知道文件的类型以后再载入相应的读写函数,而不是一开始就将所有的读写函数都载入,然后才发觉在整个程序中根本没有用到它们。

d.由于静态库在编译的时候,就将库函数装载到程序中去了,而动态库函数必须在运行的时候才装载,所以程序执行的时候,用静态库更快些。


3、Linux下用gcc制作静态链接库

(1)建立一个.c文件

示例:

#include//此处加入stdio.h头文件,是因为下面使用了printf

voidfun1()

{

printf("fun1\n");

}

intfun2(int a,int b)

{

printf("fun2\n");

return a+b;

 

}

(2)建立一个Makefile文件(注明:本人.c文件名为marc.c)

all:

gcc marc.c -o marc.o -c

ar -rc libmarc.a marc.o

第一句解释:用gcc编译生成.o文件,-c表示只编译不链接;
第二句解释:用.o文件创建libmarc.a(库名一般是lib+库名称;使用ar工具打包成.a归档文件)
(3)在Linux下make生成libmarc.a后,再创建marc.h头文件(里面存放.c函数的声明即可),再创建一个test.c文件用来测试,内容示例如下:
#include
#include "marc.h"
int main ()
{
int c;
c = fun2(3,6);
printf("c=%d\n",c);
}
(4)将步骤(3)中的3个文件放在同一个文件夹下,到Linux下用GCC执行下面命令即可正确运行
gcc test.c -o test -lmarc-L.(注意不要漏掉最后的“.”,Linux环境下表示当前目录)
参数解析:-lmarc就是告诉链接器到libmarc.a中去查找用到的函数;
               -L.表示连接器在当前目录下寻找。

备注:还有个nm命令也很有用,它可以用来查看一个.a文件中都有哪些符号
例如:GCC下输入命令nmlibmarc.a,编译器输出:
marc.o:
00000000 T fun1
00000014 T fun2
               Uputs

4.Linux下用gcc制作动态链接库
(1)和上面第1步相同;
(2)Makefile修改为:
all:
gcc marc.c -omarc.o -c -fPIC(位置无关码,.so要求为 PIC,以达到动态链接的目的
gcc -o libmarc.somarc.o -shared
//参数分析:http://blog.sina.com.cn/s/blog_54f82cc201011op1.html
(3)在Linux下make生成libmarc.so后,再创建marc.h头文件(里面存放.c函数的声明即可),再创建一个test.c文件用来测试,内容示例如下:
#include
#include "marc.h"
int main ()
{
int c;
c = fun2(3,6);
printf("c= %d\n",c);
}
(4)将步骤(3)中的3个文件放在同一个文件夹下,到Linux下用GCC执行下面命令gcc test.c -o test -lmarc-L.
,编译成功,但是运行出错,报错信息:
error while loading shared libraries: libaston.so: cannot openshared object file: No such file or directory
解决方法一:
将libmarc.so放到固定目录下就可以了,这个固定目录一般是/usr/lib目录。
cp libmarc.so /usr/lib即可
解决方法二:
使用环境变量LD_LIBRARY_PATH。操作系统在加载固定目录/usr/lib之前,会先去LD_LIBRARY_PATH这个环境变量所指定的目录下去寻找,如果找到就不用去/usr/lib下面找了,如果没找到再去/usr/lib下面找。所以解决方案就是将libmarc.so所在的目录导出到环境变量LD_LIBRARY_PATH中即可:输入pwd获取当前路径,再输入exportLD_LIBRARY_PATH=$LD_LIBRARY_PATH:(加入当前路径)即可,例如exportLD_LIBRARY_PATH=$LD_LIBRARY_PATH:/mnt/hgfs/winshare/c/4.6/dynamic_lib2_so/test。最后输入./test即可正确运行。注意:之后可输入echo$LD_LIBRARY_PATH查看当前路径。

在ubuntu中还有解决方法三,用ldconfig。
如果有root权限的话,可以修改/etc/ld.so.conf文件,然后调用 /sbin/ldconfig来达到同样的目的。不过不建议使用,因为加载太多新建库目录路径到默认指定库路径中,会拖慢系统运行速度,且限制于ubuntu下使用。

备注:使用ldd命令即使不运行test.c也可证明动态库能使用.
(ldd命令:作用是可以在一个使用了共享库的程序执行之前解析出这个程序使用了哪些共享库,并且查看这些共享库是否能被找到,能被解析(决定这个程序是否能正确执行))
 
输入ldd test,GCC输出
linux-gate.so.1 =>  (0xb76f2000)
libmarc.so (0xb76ed000)    //成功
libc.so.6 => /lib/i386-linux-gnu/libc.so.6(0xb7538000)
/lib/ld-linux.so.2 (0xb76f3000)

当输入export LD_LIBRARY_PATH= (路径为空)后,输入ldd,会输出:
linux-gate.so.1 =>  (0xb76eb000)
libmarc.so => not found  //失败
libc.so.6 => /lib/i386-linux-gnu/libc.so.6(0xb7534000)
/lib/ld-linux.so.2 (0xb76ec000)


特别注意:

在使用交叉编译器编译了一个程序后,当移植到ARM平台运行时,由于往往采用的是动态链接库,在ARM平台发现报错,说找不到文件,其实是因为程序所依赖的动态库函数在ARM平台的linux下没有,往往解决的办法有两个:

1.在PC机,编译时才有静态编译,即加-static

2.将相应的动态库文件同程序一起移植到ARM平台的linux中,并且要保证目录。

0 0