【Linux基础】 静态库和动态库的创建和使用
参考文章:《【Linux】静态库和动态库的创建与使用--示例讲解》
简短不看版
一、静态库
制作静态库:
- 方式1:
ar -crv libMytest.a test.o
(前提:已经有.o 文件) - 方式2:
g++ -c Mytest.c -o libMytest.a
(-c表示只编译,不链接)
注意:方式2使用时可能报错:g++.exe: cannot specify -o with -c or -S and multiple compilations
,意思是-o,-c同时使用的时候,只能编译一个文件,不要编译多个文件。
使用静态库:
- 方式1:将.a文件当作.o文件
gcc 1.c libMytest.a -o test
- 方式2(推荐):通过gcc -L 参数来指定库的路径 -l(小写l)指定库的名称调用, -I(大写i)指定头文件
二、动态库
制作动态库:
方式1:
步骤1:生成用于生成动态库形式的.o文件命令(-fpic):gcc -fpic -c fun1.c fun2.c
步骤2:链接生成动态库命令:gcc -shared fun1.o fun2.o -o libtest2.so
方式2:gcc -fpic -shared -o libtest2.so *.c
使用动态库:和静态库方式1、2相同。推荐使用方式2.
注意:在使用动态库之前,预先设置好环境变量 LD_LIBRARY_PATH:export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:库路径
注意:如果动态库、静态库同时存在(名字相同),编译器优先使用动态库。
一、库的种类
Linux 下的库有两种:静态库 和 动态库(共享库);
静态库:程序在编译链接的时候把库的代码链接到可执行文件中, .a 为后缀
动态库:程序在运行的时候才去链接动态库(共享库)的代码,可以多个程序共享使用库的代码,通常以.so为后缀;
二、静态库和动态库的优缺点
静态库:把库的代码载入可执行文件中,因此体积较大,当静态库中的内容更新后需要重新编译;由于编译器或链接器知道实际进入程序的代码,可以进行优化,运行速度会快一些;
静态库的优点:
- 执行速度快, 是因为静态库已经编译到可执行文件内部了
- 移植方便, 不依赖域其他的库文件
静态库的缺点:
- 耗费内存, 是由于每一个静态库的可执行程序都会加载一次
- 部署更新麻烦, 因为静态库修改以后所有的调用到这个静态库的可执行文件都需要重新编译
动态库:只在程序运行时载入内存,因此体积较小,共享库如果发生变化不需要重新编译;运行速度相对慢一些;
- 动态库把对一些库函数的链接载入推迟到程序运行的时期。
- 可以实现进程之间的资源共享。(因此动态库也称为共享库)
- 将一些程序升级变得简单。
- 甚至可以真正做到链接载入完全由程序员在程序代码中控制(显示调用)
动态库的优点:
- 节省内存,
- 部署升级更新方便, 只需替换动态库即可, 然后再重启服务.
动态库的缺点:
1 运行速度比静态库慢
2 移植性差, 需要把所有用到的动态库都移植.
三、静态库的创建与使用
1.创建静态库流程:
(1)将代码文件编译成目标文件.o
gcc test.c -o test.o
(2)通过ar工具将目标文件打包成.a静态库文件
ar -crv libMytest.a test.o
ar 是一个专门控制静态库的命令
-c:创建静态库,不管库是否存在
-r 把目录文件合并成一个静态库,如果静态库文件已经存在则更新。
-v :使用详细模式
-q 向静态库中添加目录文件
-t 查看静态库中有哪些目标文件
-d 从静态库中删除目标文件-x 把静态库展开为目标文件
2.使用静态库
共有两种方法,分别如下:
方法1: 直接调用
把静态库当作目标文件一样
gcc 1.c libMytest.a -o test
方法2:通过gcc -L 参数来指定库的路径 -l指定库的名称调用
gcc 1.c -L./ -lMytest -o test
-L:表示要连接的库所在目录
-l:指定库的名称(不需要前面的lib和扩展名.a只留下中间部分)
共享库在程序编译时并不会被连接到目标代码中, 而是在程序运行是才被载入. 不同的应用程序如果调用相同的库, 那么在内存里只需要有一份该共享库的拷贝, 规避了空间浪费问题.动态库在程序运行时才被载入, 也解决了静态库对程序的更新、部署和发布会带来麻烦. 用户只需要更新动态库即可, 增量更新. 为什么需要动态库, 其实也是静态库的特点导致.
按照习惯, 一般以”.so”做为文件后缀名. 共享库的命名一般分为三个部分:
- 前缀:lib
- 库名称:自己定义即可, 如test
- 后缀:.so
所以最终的静态库的名字应该为:libtest.so
四、共享库的制作
步骤1:生成目标文件.o, 此时要加编译选项:-fPIC(fpic)gcc -fpic -c fun1.c fun2.c
参数:-fpic创建与地址无关的编译程序(pic, position independent code), 目的就是为了能够在多个应用程序间共享.
步骤2、生成共享库, 此时要加链接器选项: -shared(指定生成动态链接库)
gcc -shared fun1.o fun2.o -o libtest2.so
五、共享库的使用
引用动态库编译成可执行文件(跟静态库方式一样):
用到的参数:
-L:指定要连接的库的所在目录
-l(小写的l):指定链接时需要的动态库, 去掉前缀和后缀
-I(大写的i): 指定main.c文件用到的头文件head.h所在的路径
gcc main.c -I./ -L./ -ltest2 -o main2
然后运行:./main2,发现竟然报错了.
分析为什么在执行的时候找不到libtest2.so库
当系统加载可执行代码时候, 能够知道其所依赖的库的名字, 但是还需要知道所依赖的库的绝对路径。此时就需要系统动态载入器(dynamic linker/loader)。
ldd命令可以查看可执行文件依赖的库文件, 执行ldd main2, 可以发现libtest2.so找不到.
对于elf格式的可执行程序,是由ld-linux.so*来完成的,
它先后搜索
elf文件的 DT_RPATH段 —>环境变量LD_LIBRARY_PATH —>/etc/ld.so.cache文件列表 — >/lib/和/usr/lib目录。
找到库文件后将其载入内存。
扩展:如何让系统找到共享库
暂时设置方式:临时设置LD_LIBRARY_PATH:export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:库路径
永久设置方式:
永久设置方法一(推荐), 把export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:库路径, 设置到∼/.bashrc文件中,然后在执行下列三种办法之一:
- 执行. ~/.bashrc使配置文件生效(第一个.后面有一个空格)。
- 执行
source ~/.bashrc
配置文件生效。 - 退出当前终端, 然后再次登陆也可以使配置文件生效。
永久设置方法二、把export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:库路径,设置到/etc/profile文件中
永久设置方法三、将其添加到 /etc/ld.so.cache文件中,编辑/etc/ld.so.conf文件, 加入库文件所在目录的路径
运行sudo ldconfig -v
, 该命令会重建/etc/ld.so.cache文件
永久设置方法四、拷贝自己制作的共享库到/lib或者/usr/lib