静态库(Archive Libraries)和动态库(Shared Object)的基本知识

24 篇文章 0 订阅

What are .a and .so files? 这里回答,即下面的文章也不错

Static, Shared Dynamic and Loadable Linux Libraries

一、库的类型

(一) 在windows中

.dll 动态库

.lib 静态库

库即为源代码的二进制文件

(二) 在linux中

.so 动态库

.a      静态库

 

(三) 静态库和动态库的优缺点

我们通常把一些公用函数制作成函数库,供其它程序使用。

函数库分为静态库和动态库两种。

静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。

动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在

 

1.什么是库

在windows平台和linux平台下都大量存在着库。

本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。

由于windows和linux的本质不同,因此二者库的二进制是不兼容的。

本文仅限于介绍linux下的库

 

2.库的种类

linux下的库有两种:静态库和共享库(动态库)。

 二者的不同点在于代码被载入的时刻不同。

静态库的代码在编译过程中已经被载入可执行程序,因此体积较大。

共享库的代码是在可执行程序运行时才载入内存的,在编译过程中仅简单的引用,因此代码体积较小。

 

3.库存在的意义

库是别人写好的现有的,成熟的,可以复用的代码,你可以使用但要记得遵守许可协议。

现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。共享库的好处是,不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例。

 

4.库文件是如何产生的在linux下

静态库的后缀是.a,它的产生分两步

Step 1.由源文件编译生成一堆.o,每个.o里都包含这个编译单元的符号表

Step 2.ar命令将很多.o转换成.a,成文静态库

动态库的后缀是.so,它由gcc加特定参数编译产生。

例如:

$ gcc -fPIC -c *.c $ gcc -shared -Wl,-soname, libfoo.so.1 -olibfoo.so.1.0 *.

 

5.库文件是如何命名的,有没有什么规范

在linux下,库文件一般放在/usr/lib和/lib下,

静态库的名字一般为libxxxx.a,其中xxxx是该lib的名称

动态库的名字一般为libxxxx.so.major.minor,xxxx是该lib的名称,major是主版本号, minor是副版本号

 

6.如何知道一个可执行程序依赖哪些库

ldd命令可以查看一个可执行程序依赖的共享库,

例如# ldd /bin/lnlibc.so.6

=> /lib/libc.so.6 (0×40021000)/lib/ld-linux.so.2

=> /lib/ld- linux.so.2 (0×40000000)

可以看到ln命令依赖于libc库和ld-linux库

 

7.可执行程序在执行的时候如何定位共享库文件

当系统加载可执行代码时候,能够知道其所依赖的库的名字,但是还需要知道绝对路径

此时就需要系统动态载入器(dynamiclinker/loader)

对于elf格式的可执行程序,是由ld-linux.so*来完成的

它先后搜索elf文件的 DT_RPATH段—环境变量LD_LIBRARY_PATH—/etc/ld.so.cache文件列表—/lib/,/usr/lib目录

找到库文件后将其载入内存

 

8.在新安装一个库之后如何让系统能够找到他

如果安装在/lib或者/usr/lib下,那么ld默认能够找到,无需其他操作。

如果安装在其他目录,需要将其添加到/etc/ld.so.cache文件中,步骤如下

1.编辑/etc/ld.so.conf文件,加入库文件所在目录的路径

2.运行ldconfig,该命令会重建/etc/ld.so.cache文件

 

############################################################

 

 linux中编译静态库(.a)和动态库(.so)的基本方法

 

(四) 静态库

 

在linux环境中, 使用ar命令创建静态库文件.如下是命令的选项:

          d -----从指定的静态库文件中删除文件

          m -----把文件移动到指定的静态库文件中

          p -----把静态库文件中指定的文件输出到标准输出

          q -----快速地把文件追加到静态库文件中

          r -----把文件插入到静态库文件中

          t -----显示静态库文件中文件的列表

          x -----从静态库文件中提取文件

      还有多个修饰符修改以上基本选项,详细请man ar 以下列出三个:

          a -----把新的目标文件(*.o)添加到静态库文件中现有文件之后

          b-----***************************************之前

          v -----使用详细模式

ar 命令的命令行格式如下:

      ar[-]{dmpqrtx}[abcfilNoPsSuvV] [membername] [count] archive files...

参数archive定义库的名称, files是库文件中包含的目标文件的清单, 用空格分隔每个文件.

 

比如创建一个静态库文件的命令如下:

      ar r libapue.a error.oerrorlog.o lockreg.o

这样就了libapue.a静态库文件, 可以用 t 选项显示包含在库中的文件

创建库文件之后,可以创建这个静态库文件的索引来帮助提高和库连接的其他程序的编译速度:

使用ranlib程序创建库的索引,索引存放在库文件内部.

      ranlib libapue.a

 

用nm程序显示存档文件的索引,它可以显示目标文件的符号

nm libapue.a | more

如果是显示目标文件的符号:

nm error.o | more

如何使用呢?如下所示:

gcc -o test test.c libapue.a

这样就可以在test.c中调用在libapue.a中的函数了.

 

 

(五) 动态库

 

1.创建共享库

     gcc -shared -o libapue.soerror.o errorlog.o

这样就创建了共享库!

2.编译共享库

    假设共享库位于当前目录(即跟程序文件相同的目录中)

gcc -o test -L. -lapue test.c

这样就编译出了不包含函数代码可执行文件了,但是但你运行时会发现linux动态加载器找不到libapue.so文件.

可以用ldd 命令查看可执行文件依赖什么共享库:

ldd test

如何才能让动态加载器发现库文件呢?有两种方法可以解决:

    1.环境变量

       exportLD_LIBRARY_PATH="$LD_LIBRARY_PATH:."

    2.修改/etc/ld.so.conf文件.

 

一般应用程序的库文件不与系统库文件放在同一个目录下,一般把应用程序的共享库文件放在/usr/local/lib下,新建一个属于自己的目录apue,然后把刚才libapue.so复制过去就行了

同时在/etc/ld.so.conf中新增一行:

/usr/local/lib/apue

 

以后在编译程序时加上编译选项:

-L /usr/local/lib/apue -lapue

 

/*

参数的配置通过mangcc可以看到

-llibrary

              连接名为 library 的 库文件.

              连接器 在 标准搜索目录 中 寻找 这个 库文件, 库文件 的 真正 名 字


(六) Linux平台gcc和动态共享库的基础知识

对大多数不从事Linux平台C语言开发的人来说,GNU gcc的一套工具和Linux平台的共享库的使用还是十分陌生的,其实我也不太熟悉,姑且写点基础知识,权当做备忘吧。 

一、GNU gcc的编译工具用法 

我们先来写一个简单的C程序:hello.c

复制代码
#include <stdio.h>  
      
void print_hello() {  
      printf("Hello World\n");  
}  
      
int main(int argc, char argv[]) {  
      print_hello();  
      return 0;  
}
复制代码

定义了一个print_hello函数,调用main函数打印Hello World。 

如何编译它呢?

?
gcc -o hello -O2 hello.c

 -o参数指定生成的可执行程序的文件名, -O2是优化级别。该命令会编译生成hello可执行程序,看看这个文件:ls -l hello   

?
-rwxr-xr-x  1 robbin users 11939 2008-11-02 13:48 hello

有11KB大小。 

看看他链接了哪些系统动态链接库,用ldd命令:

?
ldd hello 

输出信息为:

?
libc.so.6 => /lib64/tls/libc.so.6 (0x0000002a9566d000) 
/lib64/ld-linux-x86-64.so.2 (0x0000002a95556000) 

 libc是C语言标准函数库,ld是动态链接器。 

接着我们看看hello这个程序里面有哪些符号,用nm命令:

?
nm hello

 输出:

?
00000000005008f8  A __bss_start 
000000000040043c  t call_gmon_start 
...... 
00000000004004f0  T main 
0000000000500658  d p. 0 
00000000004004e0  T print_hello 
                  U puts@@GLIBC_2. 2.5 
0000000000400410  T _start

中间省略了一些,不过我们还是可以在符号表里面找到函数定义。 
hello有11KB,体积偏大,去处符号表可以给它瘦身,我们用strip命令:

?
strip hello
然后再ls -l hello,输出为:
?
-rwxr-xr-x  1 webuser users 4464 2008-11-02 13:56 hello
只有4.4KB了,瘦身效果明显! 不过这次符号表再也看不到了,nm hello,输出为:nm: hello: no symbols。

最后如果我们想从可执行程序里面提取出来一点什么文本信息的话,还可以用strings命令:

?
strings hello

输出信息为:

?
/lib64/ld-linux-x86-64.so.2 
SuSE 
libc.so.6 
puts 
__libc_start_main 
__gmon_start__ 
GLIBC_2.2.5 
t fff 
Hello World 

友情提醒一下,如果你用Java写一个HelloWorld.java,编译以后你也可以用strings窥探一番。 

二、动态共享库怎么使用 

这次我们把hello.c拆开成为两个文件:hello.c和main.c。hello.c的代码是:

#include <stdio.h>  
      
void print_hello() {  
      printf("Hello World\n");  
} 

而main.c的代码是:

int main(int argc, char argv[]) {  
      print_hello();  
      return 0;  
}

hello.c是我们的动态共享库,在hello.c里面我们声明和实现了各种公用的函数,最后main.c可以去调用这些公用函数。首先我们要把hello.c编译成为动态共享库:

?
gcc -o libhello.so -O2 -fPIC -shared hello.c

-fPIC参数声明链接库的代码段是可以共享的,-shared参数声明编译为共享库。请注意这次我们编译的共享库的名字叫做libhello.so,这也是Linux共享库的一个命名的惯例了:后缀使用so,而名称使用libxxxx格式。 

然后编译main.c的时候,我们需要更多的参数让gcc知道如何寻找共享库:

?
gcc -o main -O2 -L. -lhello main.c  

-L参数指定到哪个附加路径下面去寻找共享库,现在我们指定在当前目录下面寻找; 
-l参数指定链接到哪个共享库上面,我们传的参数hello,那么gcc就会自动链接到libhello.so这个共享库上面(注意我们上面说的libXXXX.so命名规则); 
-I参数指定到哪个附加路径下面去寻找h文件,这个我们没有使用。 

最后我们成功编译好了main,执行一下,报错:

引用
 ./main: error while loading shared libraries: libhello.so: cannot open shared object file: No such file or directory

找不到libhello.so这个共享库,怎么回事?这是因为libhello.so并不在操作系统默认的共享库的路径下面,我们可以临时指定一下链接路径:

?
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH

这样就成功了。我们用ldd main看一下:

?
libhello.so => ./libhello.so (0x0000002a9566d000) 
libc.so.6 => /lib64/tls/libc.so.6 (0x0000002a9576e000) 
/lib64/ld-linux-x86-64.so.2 (0x0000002a95556000)

这次main程序链接到了libhello.so这个共享库上面。 

三、关于Linux的动态共享库的设置 

可执行程序找不到要链接的动态共享库,这是Linux上面编译和运行程序很容易碰到的问题,通过上面的小例子,我们已经大致了解共享库的一点基本原理,接下来我们要探讨一下怎么设置程序寻找动态共享库的行为。 

Linux操作系统上面的动态共享库大致分为三类: 

1、操作系统级别的共享库和基础的系统工具库 

比方说libc.so, libz.so, libpthread.so等等,这些系统库会被放在/lib和/usr/lib目录下面,如果是64位操作系统,还会有/lib64和/usr /lib64目录。如果操作系统带有图形界面,那么还会有/usr/X11R6/lib目录,如果是64位操作系统,还有/usr/X11R6 /lib64目录。此外还可能有其他特定Linux版本的系统库目录。 

这些系统库文件的完整和版本的正确,确保了Linux上面各种程序能够正常的运行。 

2、应用程序级别的系统共享库 

并非操作系统自带,但是可能被很多应用程序所共享的库,一般会被放在/usr/local/lib和/usr/local/lib64这两个目录 下面。很多你自行编译安装的程序都会在编译的时候自动把/usr/local/lib加入gcc的-L参数,而在运行的时候自动到/usr/local /lib下面去寻找共享库。 

以上两类的动态共享库,应用程序会自动寻找到他们,并不需要你额外的设置和担心。这是为什么呢? 因为以上这些目录默认就被加入到动态链接程序的搜索路径里面了。Linux的系统共享库搜索路径定义在/etc/ld.so.conf这个配置文件里面。 这个文件的内容格式大致如下:

?
/usr/X11R6/lib64 
/usr/X11R6/lib 
/usr/local/lib 
/lib64 
/lib 
/usr/lib64 
/usr/lib 
/usr/local/lib64 
/usr/local/ImageMagick/lib 

假设我们自己编译安装的ImageMagick图形库在/usr/local/ImageMagick目录下面,并且希望其他应用程序都可以使用 ImageMagick的动态共享库,那么我们只需要把/usr/local/ImageMagick/lib目录加入/etc/ld.so.conf文 件里面,然后执行:ldconfig 命令即可。 

ldcofig将搜索以上所有的目录,为共享库建立一个缓存文件/etc/ld.so.cache。为了确认ldconfig已经搜索到ImageMagick的库,我们可以用上面介绍的strings命令从ld.so.cache里面抽取文本信息来检查一下:

?
strings /etc/ld.so.cache | grep ImageMagick

输出结果为:

?
/usr/local/ImageMagick/lib/libWand.so.10 
/usr/local/ImageMagick/lib/libWand.so 
/usr/local/ImageMagick/lib/libMagick.so.10 
/usr/local/ImageMagick/lib/libMagick.so 
/usr/local/ImageMagick/lib/libMagick++.so.10 
/usr/local/ImageMagick/lib/libMagick++.so

已经成功了! 

3、应用程序独享的动态共享库 

有很多共享库只被特定的应用程序使用,那么就没有必要加入系统库路径,以免应用程序的共享库之间发生版本冲突。因此Linux还可以通过设置环境 变量LD_LIBRARY_PATH来临时指定应用程序的共享库搜索路径,就像我们上面举的那个例子一样,我们可以在应用程序的启动脚本里面预先设置 LD_LIBRARY_PATH,指定本应用程序附加的共享库搜索路径,从而让应用程序找到它。


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值