一、动态库
1.描述
a. 动态库也叫共享库
b. 动态库把对一些库函数的链接载入推迟到程序运行的时期(runtime)
c. 可以实现进程之间的资源共享
d. 将一些程序升级变得简单
e. 甚至可以真正做到链接载入完全由程序员在程序代码中控制
f. 动态链接库的名字形式为 “libxxx.so” 后缀名为 “.so”
g. windows共享库是以 .dll 为后缀的文件
2.特
a. 在程序编译时并不会被链接到目标代码中,而是在程序运行是才被载入,系统会在动态库环境变量所指定的目录下去找对应名字的动态库
b. 在程序运行时还需要动态库存在
c. 代码体积较小
d. 不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例
生成动态库链接
生成动态链接库是直接使用 gcc 命令并且需要添加 -fPIC(-fpic) 以及 -shared 参数。
-fPIC 或 -fpic 参数的作用是使得 gcc 生成的代码是与位置无关的,也就是使用相对位置。
-shared参数的作用是告诉编译器生成一个动态链接库。
二、动态库的制作
生成动态链接库的具体步骤如下:
1.将源文件进行汇编操作,需要使用参数 -c, 还需要添加额外参数 -fpic /-fPIC
# 得到若干个 .o文件
$ gcc 源文件(*.c) -c -fpic
2.将得到的.o 文件打包成动态库,还是使用 gcc, 使用参数 -shared 指定生成动态库 (位置没有要求)
$ gcc -shared 与位置无关的目标文件(*.o) -o 动态库(libxxx.so)
3.发布动态库和头文件
# 发布
1. 提供头文件: xxx.h
2. 提供动态库: libxxx.so
三、动态库举例
1. 准备测试程序
加法计算源文件 add.c:
#include <stdio.h>
#include "head.h"
int add(int a, int b)
{
return a+b;
}
减法计算源文件 sub.c:
#include <stdio.h>
#include "head.h"
int subtract(int a, int b)
{
return a-b;
}
乘法计算源文件 mult.c:
#include <stdio.h>
#include "head.h"
int multiply(int a, int b)
{
return a*b;
}
除法计算的源文件 div.c:
#include <stdio.h>
#include "head.h"
double divide(int a, int b)
{
return (double)a/b;
}
头文件 head.h:
#ifndef _HEAD_H
#define _HEAD_H
// 加法
int add(int a, int b);
// 减法
int subtract(int a, int b);
// 乘法
int multiply(int a, int b);
// 除法
double divide(int a, int b);
#endif
测试文件 main.c:
#include <stdio.h>
#include "head.h"
int main()
{
int a = 11;
int b = 28;
printf("a = %d, b = %d\n", a, b);
printf("a + b = %d\n", add(a, b));
printf("a - b = %d\n", subtract(a, b));
printf("a * b = %d\n", multiply(a, b));
printf("a / b = %f\n", divide(a, b));
return 0;
}
2. 生成动态库
第一步:使用 gcc 将源文件进行汇编 (参数-c), 生成与位置无关的目标文件,需要使用参数 -fpic或者-fPIC
# 1. 将.c汇编得到.o, 需要额外的参数 -fpic/-fPIC
$ gcc add.c div.c mult.c sub.c -c -fpic -I ./include/
# 查看目录文件信息, 检查是否生成了目标文件
$ ls

第二步:使用 gcc 将得到的目标文件打包生成动态库,需要使用参数 -shared
# 2. 将得到 .o 打包成动态库, 使用gcc , 参数 -shared
$ gcc -shared add.o div.o mult.o sub.o -o libMyTest.so
# 检查目录中是否生成了动态库
$ ls

第三步:发布生成的动态库和相关的头文件
# 3. 发布库文件和头文件
1. head.h
2. libMyTest.so
动态库的使用
编译测试程序
# 编译测试程序
$ gcc main.c -o Test
/tmp/ccy2ionJ.o:在函数‘main’中:
main.c:(.text+0x3a):对‘add’未定义的引用
main.c:(.text+0x5c):对‘subtract’未定义的引用
main.c:(.text+0x7e):对‘multiply’未定义的引用
main.c:(.text+0xa0):对‘divide’未定义的引用
collect2: error: ld returned 1 exit status
上述错误分析:
编译的源文件中包含了头文件 head.h, 这个头文件中声明的函数对应的定义(也就是函数体实现)在动态库中,程序在编译的时候没有找到函数实现,因此提示对‘xxxxxx’未定义的引用。
解决方法:在编译的时将动态库的路径和名字都指定出来
-L: 指定库所在的目录 (相对或者绝对路径)
-l: 指定库的名字,需要掐头 (lib) 去尾 (.so) 剩下的才是需要的动态库的名字
添加库信息相关参数,重新编译测试代码:
# 在编译的时候指定动态库相关的信息: 库的路径 -L, 库的名字 -l
$ gcc main.c -o Test -L./ -lMyTest
# 查看是否生成了可执行程序
$ ls

# 执行生成的可执行程序, 错误提示 ==> 可执行程序执行的时候找不到动态库
$ ./Test
./Test: error while loading shared libraries: libMyTest.so: cannot open shared object file: No such file or directory
四、解决动态库无法加载问题
库的工作原理
静态库如何被加载:
在gcc编译程序的最后一个阶段(链接阶段),提供的静态库会被打包到可执行程序中。当可执行程序被执行,静态库中的代码也会被加载到内存中,因此不会出现静态库无法加载的问题。
动态库如何被加载:
在程序编译的最后一个阶段(链接阶段),在 gcc 命令中虽然指定了库路径 (使用参数 -L ), 但是这个路径并没有记录到可执行程序中,只是检查了这个路径下的库文件是否存在。同样对应的动态库文件也没有被打包到可执行程序中,只是在可执行程序中记录了库的名字。
可执行程序被执行起来之后,程序执行的时候会先检测需要的动态库是否可以被加载,加载不到就会提示上边的错误信息;当动态库中的函数在程序中被调用了, 这个时候动态库才加载到内存,如果不被调用就不加载;动态库的检测和内存加载操作都是由动态链接器来完成的.
动态链接器:
动态链接器是一个独立于应用程序的进程,属于操作系统,当用户的程序需要加载动态库的时候动态链接器就开始工作了,但是动态链接器根本就不知道用户通过 gcc 编译程序的时候通过参数 -L 指定的路径。
动态链接器搜索动态库的顺序(按优先级从高到低):
1.可执行文件内部的 DT_RPATH 段
2.系统的环境变量 LD_LIBRARY_PATH
3.系统动态库的缓存文件 /etc/ld.so.cache
4.存储动态库 / 静态库的系统目录 /lib/, /usr/lib 等
注意:按以上顺序进行搜索,若都没有找到,动态连接器就会提示动态库找不到的错误信息。
解决方案
1.将库路径添加到环境变量 LD_LIBRARY_PATH 中
# 自己把路径写进去就行了
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:动态库的绝对路径


注意:这种方法并不用会把动态库放到/usr/bin/中,重新打开一个终端的话,需要重新将库路径添加到环境变量 LD_LIBRARY_PATH 中
2、 拷贝动态库文件到系统库目录 /lib/ 或者 /usr/lib 中 (或者将库的软链接文件放进去)
# 库拷贝
sudo cp /home/wings/桌面/libMyTest.so /usr/lib
# 创建软连接
sudo ln -s /home/wings/桌面/libMyTest.so /usr/lib/libMyTest.so
库拷贝


创建软链接



在启动可执行程序之前,或者在设置了动态库路径之后,我们可以通过一个命令检测程序能不能够通过动态链接器加载到对应的动态库,这个命令叫做 ldd
# 语法:
$ ldd 可执行程序名
# 举例:
$ ldd Test
linux-vdso.so.1 (0x00007fff3f1f1000)
libMyTest.so => /usr/lib/libMyTest.so (0x00007fccd0496000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fccd00a5000)
/lib64/ld-linux-x86-64.so.2 (0x00007fccd089a000)
481





