这篇文档主要对Linux系统中静态库和动态库进行一个比较系统的学习。
Linux下的函数库
分类
linux下的库有两种:静态库和共享库(动态库)。
二者的不同点在于代码被载入的时刻不同:
- 静态库的代码在编译过程中已经被载入可执行程序,运行时将不再需要该静态库,因此体积较大。
- 共享库的代码是在可执行程序运行时才载入内存的,在编译过程中仅简单的引用,因此代码体积较小。
命名
在linux下,库文件一般放在/usr/lib和/lib下,
静态库的名字一般为libxxxx.a,其中xxxx是该lib的名称
动态库的名字一般为libxxxx.so.major.minor,xxxx是该lib的名称,major是主版本号, minor是副版本号
动态库的三个名字:
“real name”:实际的文件名,这是实际生成的库文件,一般命名为libXXX.so.X.Y.Z
其中XXX是自定义的名字,后面的X,Y,Z是用数字表示的版本号
- X是主版本号,一般在库的接口发生改变,无法兼容的时候,更新这个数字
- Y是次版本号,如果接口没有改变,只是升级算法,或者增加新接口,更新这个数字
- Z是build版本号,一般每升级一次加1,也可以省略
“soname”:共享库的一个特殊的名字,在每个共享库生成的时候,一般会指定这个名字
一般命名为libXXX.so.X,其实就是上面的real name去掉最后两个版本号
每一个程序,在编译的时候,会指定一个他需要的共享库的soname,程序在启动的时候,由系统自动找到一个合适的共享库来使用
从前面可以看出,X是主版本号,一般接口有改变,无法保持兼容,才会更改这个数字,所以,soname就要指定这个数字,避免由于接口改变导致运行错误
“link name”:这个是在编译可执行程序的时候,使用的名字,如果要自己编译程序,就需要关心这个名字。比如我们在编译需要用到数学库的程序时,需要加上-lm参数,这个参数的意义就是,编译器会去寻找libm.so这个共享库(或者libm.a这个静态库,这个不是本文重点),这里的libm.so就是link name,命名的规则很明显,就是把soname后面的版本也去掉,只保留libXXX.so
一般用
readelf -d libXXX.so.X.Y.Z
来查看一个共享库文件的soname
,其实real name
和soname
真正的对应关系是用这个命令来看的
Static Library
创建
详细创建过程可以参见:Linux编译器学习
|
|
Shared Library
创建
详细创建过程可以参见:Linux编译器学习
|
|
注意:
- 创建时的选项
-fpic
,-shared
,其中-fpic
还有-fPIC
形式,区别在于-fPIC
生成的库比较大,比较通用,-fpic
生成的可能受平台限制较大 - 编译链接时需要指定位置,可以使用
-L
或是添加环境变量LIBRARY_PATH
的方式来将相关库文件包含到搜索路径中,然后还需要通过-l
指定函数库,使用到gcc非标准内置函数库时需要加上-lm
选项 - 运行前需要将库函数添加到路径中,一般都是通过改变
LD_LIBRARY_PATH
来实现路径的包含,更多可见Linux环境变量及编译器库路径 - 可以通过自带的
ldd
命令来分析动态库的链接情况,如果目标程序没有链接动态库,则打印“not a dynamic executable”
对于Gfortran的一些建议
-l library
选项的命令行顺序- 将 -llibrary 选项放置在任一 .f、.for、.F、.f95 或 .o 文件之后。
- 如果调用了 libx 中的函数,并且这些函数引用了 liby 中的函数,则将 -lx 置于 -ly 之前。
-L dir
只有将其放在它所应用的 –llibrary 选项之前,该选项才有用。
库搜索路径和顺序-动态链接
在生成时指定动态库:
1f95 program.f -R/home/proj/libs -L/home/proj/libs -lmylib生成可执行文件时,链接程序会在可执行文件本身中记录共享库的路径。这些搜索路径可以用 -Rpath 选项指定。这一点与 -Ldir 选项相反,该选项在生成时指示到哪里查找 -llibrary 选项所指定的库,但不会将该路径记录到二进制可执行文件中。
在运行时指定动态库:修改
LD_LIBRARY_PATH
参考: