我们在写代码的时候,会调用很多的库函数,像printf scanf 等函数,但是我们的代码中并没有这些函数的实现,这些函数是语言提供的函数,这些函数就存放到对应的库中,而这些库就叫做动态库和静态库。
- 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
- 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库
动态库在程序运行的时候,运行到了库函数的函数入口,而我们没有函数的实现,这时程序就会到对应的动态库里去链接动态库里这个函数的实现代码,把库中的代码拿到虚拟地址空间中的共享区,执行代码。
优点:不需要把库函数的代码编译进我们的可执行程序,可执行程序的体积小,多份代码可以共用一个库,节省了空间。如果库函数更新,在进行升级的时候不需要重新编译程序,直接升级库即可。
缺点:对库的依赖性强,如果没有动态库,那么这个用了动态库的可执行程序将无法运行。
静态库是程序在编译链接阶段,就把程序所需的库函数代码,拷贝到程序中一起编译,这时的程序不再需要静态库就能运行。所需要的库中的代码是直接被拿到代码段执行的,和整个可执行程序是一体的。
优点:编译成功的可执行程序可以独立运行,不再需要静态库,没有了库的依赖性。
缺点:生成的程序占用空间大,不能多个程序共用一个库,库升级的时候需要重新编译程序。
动态库和静态库的本质区别就是看库是否被编译到了程序内部
首先,一个静态库,其实就是自己写了一些方法,我们把这些方法的源文件和已经编译汇编好的.o文件打包起来,打包好的文件就是库文件,别人只需要配合.h头文件和静态库文件进行编译生成可执行程序,就能使用我们写的方法。
ar -rc libmymath.a add.o sub.o
# ar是gnu归档工具,rc表示(replace and create)
查看静态库中的目录列表
ar -tv libmymath.a
# t:列出静态库中的文件
# v:verbose 详细信息
一般我们在给别人提交一个库文件时,会将库文件和头文件放到一个目录结构中,如下
include中放库所需的头文件,lib中放库文件。
而前面生成的.o文件和源文件就没有用了。
在makefile中填写编译指令
gcc -o $@ $^ -I ./mylib/include -L ./mylib/lib -l mymath -static
# -I 表示头文件的存放路径(头文件搜索路径)
# -L 表示库文件的存放路径(库文件搜索路径)
# -l 表示库文件的具体名称(库文件名,静态库去掉lib前缀和.a后缀即为文件名)
在后面加上**-static**表示静态链接编译
编译执行,可以发现程序正常执行
动态库的结构和静态库类似,都是需要.o文件和.h文件。
libmymath.so:myadd.o mysub.o
gcc -shared -o $@ $^
# -shared表示生成共享库格式
myadd.o:myadd.c
gcc -fPIC -c $<
# fPIC:产生位置无关码(position independent code)
mysub.o:mysub.c
gcc -fPIC -c $<
.PHONY:clean
clean:
rm *.o libmymath.so
.PHONY:output
output:
mkdir -p mylib/include
mkdir -p mylib/lib
cp *.h mylib/include
cp *.so mylib/lib
- shared: 表示生成共享库格式
- fPIC:产生位置无关码(position independent code)
- 库名规则:libxxx.so
运行makefile
至此,我们就把我们的程序和头文件打包成了一个动态库 并且把动态库放进了发布文件
mytestso:mytestso.c
gcc -o $@ $^ -I ./mylibso/mylib/include -L ./mylibso/mylib/lib -l mymath
.PHONY:clean
clean:
rm mytestso
和静态库的编译类似,需要设置头文件和库文件的搜索路径 但是末尾不用加static
编译通过,但是运行时显示找不到我们的动态库
使用ldd命令查看可执行文件的依赖库,发现not found =
这是因为我们编译时设置的搜索路径是把路径告诉了gcc编译器,但是动态库是在程序运行时才去找库,操作系统并不知道我们的库文件放在哪,这时就要设置环境变量来告诉操作系统我们的库位置
export LD_LIBRARY_PATH=/home/xiaomage/workspace/Lib/mylibso/mylib/lib
这时再用ldd查看依赖库,发现已经找到库路径
执行成功