linux下的库入门

一、什么是库

       在 linux 平台存在着大量的库,库的本质是一种可执行的二进制代码(但不可以独立执行)集合,可以通过操作系统将其载入内存执行。windows 和 linux 的平台的差异性(预编译、编译器、汇编器和链接器的不同),因此二者库的二进制版本也是无法兼容。

二、 库的种类

  linux 下的库有两种:静态库和动态库(共享库),他们的主要差别在被调用者调用时时代码被载入的时机不同。

  静态库的特征:1、以.a为后缀;2、前缀名以lib开始;3、生成的可执行程序体积较大;4、代码在编译过程中就会被加载进可执行程序程序中。

  共享库(动态库)的特征:1、用.so为后缀;2、前缀名以lib开始;3、生成的可执行程序代码体积较小;4、代码是在可执行程序运行中调用时才载入内存的,在编译过程中仅简单的引用。

  共享库(动态库)的优势: 若在不同的应用程序中如果调用相同的库,那么只会在内存里存在一份共享库的实例即可;可以在系统中存在库的多个版本。使用者需要自己搞定共享库问题。

  静态库的优势:若在不同的应用程序中如果调用相同的库,那么会在内存里存在多份共享库的实例;无需考虑库的问题。

      为了在同一系统中使用不同版本的库,可以在库文件名后加上版本号为后缀,例如: libhello.so.1.0,由于程序连接默认以.so为文件后缀名。所以为了使用这些库,通常使用建立符号连接的方式。

      ln -s libhello.so.1.31 libhello.so.1 

      ln -s libhello.so.1 libhello.so

代码如下:

#vector.h 

1 #ifndef vector_h
 2 #define vector_h
 3 
 4 void addVec(int *xP, int *yP, int *zP, int Num);
 5 void mulVec(int *xP, int *yP, int *zP, int Num);                                        
 6 
 7 #endif

#addVec.c

 1 #include "vector.h"
 2                                                                                         
 3 void addVec(int *xP, int *yP, int *zP, int Num){
 4     for(int i = 0; i < n; i++){
 5         zP[i] = xP[i] + yP[i];
 6     }
 7 
 8     return;
 9 }
~   

#mulVec.c

 1 #include "vector.h"                                                                     
 2 
 3 void mulVec(int *xP, int *yP, int *zP, int Num){
 4     for(int i = 0; i < n; i++){
 5         zP[i] = xP[i] * yP[i];
 6     }
 7 
 8     return;
 9 }


#testVec.c

 1 #include <stdio.h>                                                                      
 2 #include "vector.h"
 3 
 4 int x[2] = {1, 2};
 5 int y[2] = {3, 4};
 6 int z[2];
 7 
 8 int main(int argc, char **argv)
 9 {
10     addVec(x, y, z, 2);
11     printf("z = [%d %d]\n", z[0],  z[1]);
12 
13     return 0;
14 }

三、生成静态库文件:

  1、gcc -Og -c *Vec.c            //生成目标文件

  2、ls *.o  或  ls | grep .o             //查看目标文件

  3、ar crs libVector.a addVec.o mulVec.o       //生成静态库文件

  4、ls *.a                   //查看生成的库文件

  5、file libVector.a                  //查看文件类型

  6、ar -t libVector.a                //查看文件内容

四、动态库的生成:

  1、gcc -shared -fPIC -o libVector.so addVec.c mulVec.c     //生成动态库文件

  2、ls *.so  或  ls | grep .so                  //查看目标文件

  3、file libVector.a                       //查看文件类型

  4、ldd libVector.so                     //查看生成的库文件

  5、nm libVector.so                     //查看生成的库文件包含哪些函数 

五、库文件的其他

1、库文件的规范: linux中,库文件常放在/usr/lib和/lib下, 其中静态库名字常为libxxxx.a,其中xxxx是该库文件的名称;动态库的名字常为libxxxx.so,xxxx 是该库的名称,有的库会有主副等版本号

2、定位共享库的时机 :当系统加载可执行代码(即库文件)的时候,能够知道其所依赖的库的名字,但是还需要知道绝对路径,此时就需要系统动态载入器(dynamic linker/loader) ,对linux下的elf 格式的可执行程序,是由 ld-linux.so* 来完成的,它先后搜索 elf 文件的 DT_RPATH 段-->环境变量LD_LIBRARY_PATH—->/etc/ld.so.cache 文件列表--> /lib/,/usr/lib 目录找到库文件后将其载入内存。如: export LD_LIBRARY_PATH=’pwd’ ,将当前文件目录添加为共享目录。

3、使用ldd工具,查看可执行程序依赖那些动态库或着动态库依赖于那些动态库:

   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 库  
1.7、使用nm工具,查看静态库和动态库中有那些函数名;
    (T类表示函数是当前库中定义的,U类表示函数是被调用的,在其它库中定义的,W类是当前库中定义,被其它库中的函数覆盖)。
    有时候可能需要查看一个库中到底有哪些函数,nm工具可以打印出库中的涉及到的所有符号,这里的库既可以是静态的也可以是动态的。

  nm列出的符号有很多, 常见的有三种::

T类:是在库中定义的函数,用T表示,这是最常见的;

U类:是在库中被调用,但并没有在库中定义(表明需要其他库支持),用U表示;

W类:是所谓的“弱态”符号,它们虽然在库中被定义,但是可能被其他库中的同名符号覆盖,用W表示。

例如,假设开发者希望知道上文提到的hello库中是否引用了 printf():

   nm libhello.so | grep printf 

发现printf是U类符号,说明printf被引用,但是并没有在库中定义。

由此可以推断,要正常使用hello库,必须有其它库支持,使用ldd工具查看hello依赖于哪些库:

ldd libhello.so

libc.so.6=>/lib/libc.so.6(0x400la000)

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

从上面的结果可以继续查看printf最终在哪里被定义,有兴趣可以深入研究

 

1.8、使用ar工具,可以生成静态库,同时可以查看静态库中包含那些.o文件,即有那些源文件构成。

可以使用 ar -t libname.a 来查看一个静态库由那些.o文件构成。

可以使用 ar q libname.a xxx1.o xxx2.o xxx3.o ... xxxn.o 生成静态库

 

1.9、file查看动态库和静态库是32位,还是64位下的库:

如果是动态库,可以使用file *.so;

如果是静态哭,可以使用objdump -x *.a 

 
posted @ 2020-12-20 22:05  叕叒双又  阅读(224)  评论(0编辑  收藏  举报