1. 动态库和静态库
1.1 什么是动态库、静态库:
- 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库.
- 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
- 一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码.
- 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking).
- 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。
测试程序 /add.h/ #ifndef __ADD_H__ #define __ADD_H__ int add(int a, int b); #endif // __ADD_H__ /add.c/ #include "add.h" int add(int a, int b) { return a + b; } /sub.h/ #ifndef __SUB_H__ #define __SUB_H__ int sub(int a, int b); #endif // __SUB_H__ /add.c/ #include "add.h" int sub(int a, int b) { return a - b; } ///main.c #include <stdio.h> #include "add.h" #include "sub.h" int main(void) { int a = 10; int b = 20; printf("add(10, 20)=%d\n", a, b, add(a, b)); a = 100; b = 20; printf("sub(%d,%d)=%d\n", a, b, sub(a, b)); }
1.2 静态库
**1.2.1 生成静态库 **
通过写程序生成静态库:
如法炮制,再创建myprintf.h、myprintf.c:
** 这里介绍几种在Linux环境下获取时间打印的方法:**
这里我们使用man 3 time。
通过以上准备,我们实现了累加功能和打印功能!
(1)将所有的源文件编译成 .o文件:
那么如果我们将我们形成的 .o 文件给其他人,其他人能够链接使用吗?
这里我们验证一下:
因此如果我们将我们形成的 .o 文件给其他人,其他人能够链接使用!
回到我们的mklib:
我们需要将自己所有的 .o 文件归档:Archive files(文件归档)
libmymath.a:mymath.o myprint.o ar -rc libmymath.a mymath.o myprint.o mymath.o:mymath.c gcc -c mymath.c -o mymath.o myprint.o:myprint.c gcc -c myprint.c -o myprint.o .PHONY:clean clean: rm -f *.o *.a
**1.2.2 发布静态库 **
libmymath.a:mymath.o myprint.o ar -rc libmymath.a mymath.o myprint.o mymath.o:mymath.c gcc -c mymath.c -o mymath.o -std=c99 myprint.o:myprint.c gcc -c myprint.c -o myprint.o .PHONY:static static: mkdir -p lib-static/lib mkdir -p lib-static/include cp *.a lib-static/lib cp *.h lib-static/include .PHONY:clean clean: rm -rf *.o *.a lib-static
以上就是生成的静态库及方法! 静态库中的 .o是与位置有关码,不能在程序任意位置被加载!(这里与下面的动态库fPIC :与位置无关码作比较记忆理解)
[root@localhost linux]# ls add.c add.h main.c sub.c sub.h [root@localhost linux]# gcc -c add.c -o add.o [root@localhost linux]# gcc -c sub.c -o sub.o 生成静态库 [root@localhost linux]# ar -rc libmymath.a add.o sub.o ar是gnu归档工具,rc表示(replace and create) 查看静态库中的目录列表 [root@localhost linux]# ar -tv libmymath.a rw-r--r-- 0/0 1240 Sep 15 16:53 2017 add.o rw-r--r-- 0/0 1240 Sep 15 16:53 2017 sub.o t:列出静态库中的文件 v:verbose 详细信息 [root@localhost linux]# gcc main.c -L. -lmymath -L 指定库路径 -l 指定库名 测试目标文件生成后,静态库删掉,程序照样可以运行。
**1.2.3 库搜索路径 **
- 从左到右搜索-L指定的目录。
- 由环境变量指定的目录 (LIBRARY_PATH)
- 由系统指定的目录
- /usr/lib/usr/local/lib
1.3 动态库
1.3.1 生成动态库
- shared: 表示生成共享库格式
- fPIC:产生位置无关码(position independent code)
- 库名规则:libxxx.so
示例: [root@localhost linux]# gcc -fPIC -c sub.c add.c [root@localhost linux]# gcc -shared -o libmymath.so*.o [root@localhost linux]# ls add.c add.h add.o libmymath.so main.c sub.c sub.h sub.o
fPIC 使得形成的 .o文件可以加载内存中的任意位置使用。因为它采用的是起始地址+ 偏移量的方式形成的相对地址
1.下面我们从设计库的工程师角度,生成库、发送库:
#动态库的生成、打包、发送 libmymath.so:mymath.o myprint.o gcc -shared -o libmymath.so mymath.o myprint.o mymath.o:mymath.c gcc -fPIC -c mymath.c -o mymath.o -std=c99 myprint.o:myprint.c gcc -fPIC -c myprint.c -o myprint.o .PHONY:clean clean: rm -rf *.o *.so #静态库的生成、打包、发送 #libmymath.a:mymath.o myprint.o # ar -rc libmymath.a mymath.o myprint.o #mymath.o:mymath.c # gcc -c mymath.c -o mymath.o -std=c99 #myprint.o:myprint.c # gcc -c myprint.c -o myprint.o # #.PHONY:static #static: # mkdir -p lib-static/lib # mkdir -p lib-static/include # cp *.a lib-static/lib # cp *.h lib-static/include # # #.PHONY:clean #clean: # rm -rf *.o *.a lib-static
1.3.2 发送动态库
#动态库的生成、打包、发送 libmymath.so:mymath.o myprint.o gcc -shared -o libmymath.so mymath.o myprint.o mymath.o:mymath.c gcc -fPIC -c mymath.c -o mymath.o -std=c99 myprint.o:myprint.c gcc -fPIC -c myprint.c -o myprint.o .PHONY:dyl dyl: mkdir -p lib-dyl/lib mkdir -p lib-dyl/include cp *.so lib-dyl/lib cp *.h lib-dyl/include .PHONY:clean clean: rm -rf *.o *.so lib-dyl #静态库的生成、打包、发送 #libmymath.a:mymath.o myprint.o # ar -rc libmymath.a mymath.o myprint.o #mymath.o:mymath.c # gcc -c mymath.c -o mymath.o -std=c99 #myprint.o:myprint.c # gcc -c myprint.c -o myprint.o # #.PHONY:static #static: # mkdir -p lib-static/lib # mkdir -p lib-static/include # cp *.a lib-static/lib # cp *.h lib-static/include # # #.PHONY:clean #clean: # rm -rf *.o *.a lib-static
2.同时生成动、静态库并打包发送
#同时形成动态库、静态库 .PHONY:all all: libmymath.so libmymath.a #形成动态库 libmymath.so:mymath.o myprint.o gcc -shared -o libmymath.so mymath.o myprint.o mymath.o:mymath.c gcc -fPIC -c mymath.c -o mymath.o -std=c99 myprint.o:myprint.c gcc -fPIC -c myprint.c -o myprint.o #形成静态库,这里的依赖关系已标记为 *_s.o 为标记的 .o文件 #防止与动态库使用的.o 文件冲突 libmymath.a:mymath_s.o myprint_s.o ar -rc libmymath.a mymath_s.o myprint_s.o mymath_s.o:mymath.c gcc -c mymath.c -o mymath_s.o -std=c99 myprint_s.o:myprint.c gcc -c myprint.c -o myprint_s.o .PHONY:lib lib: #发送动态库 mkdir -p lib-dyl/lib mkdir -p lib-dyl/include cp *.so lib-dyl/lib cp *.h lib-dyl/include #发送静态库 mkdir -p lib-static/lib mkdir -p lib-static/include cp *.a lib-static/lib cp *.h lib-static/include .PHONY:clean clean: rm -rf *.o *.a *.so lib-static lib-dyl ##动态库的生成、打包、发送 #libmymath.so:mymath.o myprint.o # gcc -shared -o libmymath.so mymath.o myprint.o #mymath.o:mymath.c # gcc -fPIC -c mymath.c -o mymath.o -std=c99 #myprint.o:myprint.c # gcc -fPIC -c myprint.c -o myprint.o # #.PHONY:dyl #dyl: # mkdir -p lib-dyl/lib # mkdir -p lib-dyl/include # cp *.so lib-dyl/lib # cp *.h lib-dyl/include # #.PHONY:clean #clean: # rm -rf *.o *.so lib-dyl #静态库的生成、打包、发送 #libmymath.a:mymath.o myprint.o # ar -rc libmymath.a mymath.o myprint.o #mymath.o:mymath.c # gcc -c mymath.c -o mymath.o -std=c99 #myprint.o:myprint.c # gcc -c myprint.c -o myprint.o # #.PHONY:static #static: # mkdir -p lib-static/lib # mkdir -p lib-static/include # cp *.a lib-static/lib # cp *.h lib-static/include # # #.PHONY:clean #clean: # rm -rf *.o *.a lib-static
以上是站在设计库的工程师角度,生成库、发送库!
接下来我们站在用户的角度使用库!
3. 使用动、静态库
编译选项
- l:链接动态库,只要库名即可(去掉lib以及版本号)
- L:链接库所在的路径.
示例: gcc main.o -o main –L. -lhello
3.1 回顾头文件的搜索路径的方式:
“ ” 和 < >
- ” ”:在当前路径下查找头文件
- < > :在系统头文件路径下查找头文件
我们可知,我们这里的头文件是在当前路径下,而谁在找头文件呢? 是编译器! 在Linux中,是gcc的进程在找!
那么我们如何解决上面找不到头文件的问题?
查找头文件的方法:
3.2 测试静态库:
(1)将自己的头文件和库文件拷贝至系统路径下:
-lmymath:表示link mymath库!
注:
将自己的头文件和库文件拷贝至系统路径下成为库的安装!
gcc -l(指明要链接的第三方库的名称)
但是不推荐以上做法,因为自己写的代码质量不一定高!放到系统路径共享使用不够资格!
会污染系统头文件!
因此我们这里将它删除: (卸载库)
不推荐使用安装库,那么使用什么方式呢?
(2)指定头文件搜索路径:
-I:头文件查找的路径
-L: 库文件搜索的路径
-l: 在-L指定的路径下,要链接的库如果安装到系统,就不会这里麻烦!
3.3 测试动态库:
3.3.1动态库的搜索路径:
在 执行 ./mytest时,我们发现报错(这里的报错非正确报错,这台机器的环境变量被修改过,实际的报错应该是 :
./mytest: error while loading shared libraries:libmymath.so:cannot open shared object file:No such file or directory
./mytest: error while loading shared libraries : libmymath.so : cannot open shared object file: No such file or directory)
说明进程找不到对应的动态库。
3.3.2 查找动态库位置:
那么如何解决呢?
(1)动态库拷贝至系统路径下:/lib64 ——即安装库(不推荐)
(2)通过导入环境变量的方式——程序运行的时候,会在环境变量中查找自己需要的动态库路径——LD_LIBRARY_PATH
(3)系统配置文件
但是这种方式会有一个问题:那就是当关闭终端时,以上的操作就不存在了,即擦除了,只允许当前配置,当前操作!
那么我们就需要通过配置系统文件的方式来解决!
ls /etc/ld.so.conf.d/
表明的是如果系统里自定义了动态库,那么在扫描系统的路径时,除了扫描对应库路径的库之外,还会扫描读取这个里面的配置文件,根据这个配置文件里面的内容找到对应的动态库
(4)其他方式(在系统当中建立软链接)
以上就是查找动态库的几种方式!
3.4 查看系统当中的头文件:3.5 查看系统中的库:
4. 运行动、静态库
- 1、拷贝.so文件到系统共享库路径下, 一般指/usr/lib
- 2、更改 LD_LIBRARY_PATH
[root@localhost linux]# export LD_LIBRARY_PATH=. [root@localhost linux]# gcc main.c -lmymath [root@localhost linux]# ./a.out add(10, 20)=30 sub(100, 20)=80
3、ldconfifig 配置/etc/ld.so.conf.d/,ldconfifig更新
[root@localhost linux]# cat /etc/ld.so.conf.d/bit.conf /root/tools/linux [root@localhost linux]# ldconfig
5. 总结动、静态库的特点
1.静态库为什么没有这个问题呢?
因为在形成可执行程序之后,已经把需要的代码拷贝进我的代码中,运行时不再依赖你的库!不需要运行时查找!
- 动态库的这个问题是因为程序和动态库是分开加载的!
6. 库文件名称和引入库的名称
如:libc.so -> c库,去掉前缀lib,去掉后缀.so,.a
**7.使用外部库 **
系统中其实有很多库,它们通常由一组互相关联的用来完成某项常见工作的函数构成。比如用来处理屏幕显示情况的函数(ncurses库)
#include <math.h> #include <stdio.h> int main(void) { double x = pow(2.0, 3.0); printf("The cubed is %f\n", x); return 0; } gcc -Wall calc.c -o calc -lm
-lm表示要链接libm.so或者libm.a库文件
可以去使用一些库,例如:ncurses、boost库等
后记:
●由于作者水平有限,文章难免存在谬误之处,敬请读者斧正,俚语成篇,恳望指教!
——By 作者:新晓·故知
版权归原作者 新晓·故知 所有, 如有侵权,请联系我们删除。