库的定义:
1、库用于将相似函数打包在一个单元中,然后这些单元就可为其他开发人员所共享,并因此有了模块化编程这种说法。即,从模块中构建程序;
2、库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。
库的分类:1、动态库 2、静态库
使用动态库、静态库可以隐藏内部源文件文件的一些细节,加密文件。Linux 支持两种类型的库。Linux系统有几个重要的目录存放相应的函数库,如/lib, /usr/lib; 头文件放在/usr/include。
静态库:这类库的名字一般是libxxx.a,静态库包含在编译时静态绑定到一个程序的函数。
优点:静态函数库编译成的文件比较大,因为整个函数库的所有数据都被整合进目标代码中,即编译后的执行程序不需要外部的函数库支持,因为所有使用的函数都已经被编译进可执行文件了。
缺点:如果静态函数库改变了,那么你的程序必须重新编译,而且体积也较大。
动态库:这类库的名字一般是libxxx.so,动态库又称共享库;它是在加载应用程序时被加载的,而且它与应用程序是在运行时绑定的。
优点:相对于静态函数库,动态函数库在编译的时候并没有被编译进目标代码中,你的程序执行到相关函数时才调用函数库里的相应函数,因此动态函数库所产生的可执行文件比较小。由于函数库没有被整合进你的程序,而是程序运行时动态申请并调用,所以程序的运行环境中必须提供相应的库。动态函数库的改变并不影响你的程序,所以动态函数库的升级比较方便。如果多个应用程序都要使用同一函数库,动态库就非常适合,可以减少应用程序的体积。
在既有动态库和静态库的情况下默认使用动态库。
静态库制作:
$gcc -c file1.c$gcc -c file2.c
....
$gcc -c fileN.c
$ar -rcs libname.a file1.o file2.o ... fileN.o
动态库制作:
$gcc -shared -fpic -o libname.so file1.c file2.c ... fileN.c编译:
$gcc main.c -o test_lib -L lib_path -lname编译、链接、运行案例及问题:
编译:
$ gcc main.c
main.c:14:20: error: crypto.h: No such file or directory
如果静态库编译时出现下列问题该如何解决?
$:gcc main.c
/tmp/cc585Kep.o:In function `main'
main.c:(.test+0x1f):underined reference to`init_list'
main.c:(.test+0x3f):underined reference to`insert_list_head'
main.c:(.test+0x5f):underined reference to`insert_list_tail'
main.c:(.test+0x77):underined reference to`traval_list'
main.c:(.test+0x88):underined reference to`traval_list'
main.c:(.test+0x94):underined reference to`destroy_list'
collect2:ld returned 1 exit status
ld returned表示链接错误
underined reference to表示它在编译这个函数时,main程序最终要编译成可执行程序,但是这个的时候找不到相应函数。
一旦出现这个问题,说明相应的库没有被链接进来。
解决方案:
$gcc main.c -L. -lzhangsan -I.
链接:
$ gcc -static main.c -o app -L./libs -lcrypto -lfunc
/tmp/ccfrMcUo.o: In function `main':
main.c:(.text+0xa): undefined reference to `crypt0'
collect2: ld returned 1 exit status
-L 指定库的路径 第一个-l (小写字母l) 指定库的名字,注意后面直接跟库名,没有空格 第二个-I(大写字母i) 指定头文件路径 .当前路径(0我做的时候是在当前路径下,所以用了.)
动态库和静态库同时存在,默认是使用动态库。如果要用静态库,加上-static链接选项。
如果动态编译的时候出现下列问题?
$gcc main.c -L. -lxiaoming I.
$./a.out
./a.out: error while loading shared libraries: libxiaoming.so: cannot open shared object file: No such file or directory
不能打开这个文件
解决方案:
1、把库拷贝到/usr/lib/下(库存放在这里),注意拷贝过去要有权限,只有root才可以做这件事。
2、export LD_LIBRARY.PATH=.(通过这个环境变量来告诉它,后面跟路径名。我这里是当前路径下,所以用".")
LD_LIBRARY_PATH: 告诉系统执行程序的时候,除了/lib, /usr/lib以外还到哪里找动态库。
PATH: 告诉系统执行的Linux命令查找的路径
文件编译之后就叫做链接,有两种链接:1、静态链接 2、动态链接c
静态链接:静态链接会把静态库中可执行文件的代码段细节函数全部拷贝到可执行文件中。
动态链接:不会把动态库中可执行文件中的代码段细节函数拷贝到可执行文件中。
例如:
$vim test.c
#include<stdio.h>
int main(int argc,char **argv)
{
int a=MAX;
int b=0;
printf("a=%d b=%d %s\n",a,b,FILE);
return 0;
}
动态编译:
$gcc test.c
执行:
$./a.out
看文件有多大
$ll a.out
-rwxrwxr-x 1 lisi lisi 6482 Sep 30 20:12 a.out
静态编译:
$gcc text.c -static
-rwxrwx-r-x 1 lisi lisi 76220 Sep 30 20:13 a.out
很明显动态编译出来的文件比静态编译出来的小的多(6482<<76220)
给他们取个名字:
$gcc test.c -o 1
$gcc test.c -o -static
$feil 1
1: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped
$feil 2
2: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.18, not stripped
注意:
dynamically linked (uses shared libs):动态链接,对于以上a.out这个程序中,只保存了这个信息,只希望函数在运行过程中需要一个库来激活ptintf函数,printf函数的代码不会被拷贝过来,当./a.out执行的时候执行,操作系统自动会把printf依赖的动态库加载到内存上执行,叫动态加载。
statically linked:静态链接,它编译的时候,他会把所有的printf所依赖的代码加载到a.out这个程序中去,然后执行。
结论:静态链接执行不需要动态库,动态链接执行需要动态库。
linux进行动态链接:
让我们深入探讨一下使用 Linux 中的动态链接的共享库的过程。当用户启动一个应用程序时,内核首先将 ELF 映像加载到用户空间虚拟内存中,然后内核会注意到一个称为 .interp 的 ELF 部分,它指明了将要被使用的动态链接器(/lib/ld-linux.so),这与脚本文件的解释器定义(#!/bin/sh)很相似。
ld-linux.so 本身就是一个 ELF 共享库,但它是静态编译的并且不具备共享库依赖项。当需要动态链接时,内核会引导动态链接(ELF 解释器),该链接首先会初始化自身,然后加载指定的共享对象(已加载则不必)。接着它会执行必要的再定位,包括目标共享对象所使用的共享对象。LD_LIBRARY_PATH 环境变量定义查找可用共享对象的位置。定义完成后,控制权会被传回到初始程序以开始执行。
再定位是通过一个称为 Global Offset Table(GOT)和 Procedure Linkage Table(PLT)的间接机制来处理的。这些表格提供了 ld-linux.so 在再定位过程中加载的外部函数和数据的地址。这意味着无需改动需要间接机制(即,使用这些表格)的代码:只需要调整这些表格。一旦进行加载,或者只要需要给定的函数,就可以发生再定位(稍候在 用 Linux 进行动态加载 小节中会看到更多的差别)。
再定位完成后,动态链接器就会允许任何加载的共享程序来执行可选的初始化代码。该函数允许库来初始化内部数据并备之待用。这个代码是在上述 ELF 映像的 .init 部分中定义的。在卸载库时,它还可以调用一个终止函数(定义为映像的 .fini 部分)。当初始化函数被调用时,动态链接器会把控制权转让给加载的原始映像。