库和库链接
本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。库是写好的现有的,成熟的,可以复用的代码,现实中每个程序都要依赖很多基础的底层库。
库有两种类型:
-
静态库:.a文件(win 系统下是lib)
-
动态库:.so文件(win 系统下是.dll)
静态库
静态库实际上是一些目标文件的集合,只用于链接生成可执行文件阶段。链接器会将程序中使用到函数的代码从库文件中拷贝到应用程序中,一旦链接完成生成可执行文件之后,在执行程序的时候就不需要静态库了。
静态库库的特点:
- 静态库对函数库的链接是放在编译时期完成的。
- 程序在运行时与函数库再无瓜葛,移植方便。
- 浪费空间和资源,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件。
- 是静态库对程序的更新、部署和发布页会带来麻烦。如果静态库liba.lib更新了,所以使用它的应用程序都需要重新编译、发布给用户(对于玩家来说,可能是一个很小的改动,却导致整个程序重新下载,全量更新)。
动态库
动态库也叫共享库(share object),在程序链接的时候只是作些标记,然后在程序开始启动运行的时候,动态地加载所需库(模块)。
动态库特点:
- 动态库把对一些库函数的链接载入推迟到程序运行的时期。
- 可以实现进程之间的资源共享。(因此动态库也称为共享库)
- 将一些程序升级变得简单。
- 甚至可以真正做到链接载入完全由程序员在程序代码中控制(显示调用)。
库链接
库链接主要是将有关的目标文件彼此相连接生成可加载、可执行的目标文件。链接器的核心工作就是符号表解析和重定位。
链接的时机:
- 编译时,就是源代码被编译成机器代码时(静态链接器负责)
- 加载时,也就是程序被加载到内存时(加载器负责)
- 运行时,由应用程序来实施(动态链接器负责)
静态链接(编译时)
链接器将函数的代码从其所在地(目标文件或静态链接库中)拷贝到最终的可执行程序中。这样该程序在被执行时这些代码将被装入到该进程的虚拟地址空间中。静态链接库实际上是一个目标文件的集合,其中的每个文件含有库中的一个或者一组相关函数的代码。
动态链接(加载、运行时)
在编译的链接阶段,动态链接库只提供符号表和其他少量信息用于保证所有符号引用都有定义,保证编译顺利通过。动态链接器(ld-linux.so)链接程序在运行过程中根据记录的共享对象的符号定义来动态加载共享库,然后完成重定位。
configure
源码安装过程中大多会用到configure这个程序,一般的configure都是一个script,执行时可以传入必要参数告知配置项目。
configure程序它会根据传入的配置项目检查程序编译时所依赖的环境以及对程序编译安装进行配置,最终生成编译所需的Makefile文件供程序Make读入使用进而调用相关编译程式(通常调用编译程序都是gcc)来编译最终的二进制程序。而configure脚本在检查相应依赖环境时(例:所依赖软件的版本、相应库版本等),通常会通过pkg-config的工具来检测相应依赖环境。
pkg-config
一般来说,如果库的头文件不在 /usr/include 目录中,那么在编译的时候需要用 -I 参数指定其路径。由于同一个库在不同系统上可能位于不同的目录下,用户安装库的时候也可以将库安装在不同的目录下,所以即使使用同一个库,由于库的路径的不同,造成了用-I参数指定的头文件的路径和在连接时使用-L参数指定lib库的路径都可能不同,其结果就是造成了编译命令界面的不统一。可能由于编译,连接的不一致,造成同一份程序从一台机器copy到另一台机器时就可能会出现问题。
pkg-config就是用来解决编译连接界面不统一问题的一个工具。pkg-config是通过库提供的一个 .pc文件 获得库的各种必要信息的,包括版本信息、编译和连接需要的参数等。需要的时候可以通过pkg-config提供的参数(–cflags, –libs),将所需信息提取出来供编译和连接使用。这样,不管库文件安装在哪,通过库对应的.pc文件就可以准确定位,可以使用相同的编译和连接命令,使得编译和连接界面统一。
它提供的主要功能有:
- 检查库的版本号。如果所需库的版本不满足要求,打印出错误信息,避免连接错误版本的库文件。
- 获得编译预处理参数,如宏定义,头文件的路径。
- 获得编译参数,如库及其依赖的其他库的位置,文件名及其他一些连接参数。
- 自动加入所依赖的其他库的设置。
使用pkg-config工具提取库的编译和连接参数有两个基本的前提:
- 库本身在安装的时候必须提供一个相应的.pc文件。不这样做的库说明不支持pkg-config工具的使用。
- pkg-config必须知道要到哪里去寻找此.pc文件
在默认情况下,每个支持pkg-config的库对应的.pc文件在安装后都位于安装目录中的 lib/pkgconfig 目录下。有些软件安装后的.pc文件存在于 /usr/lib/pkgconfig 或者 /usr/lib/pkgconfig
PKG_CONFIG_PATH
环境变量PKG_CONFIG_PATH是用来设置.pc文件的搜索路径的,pkg-config按照设置路径的先后顺序进行搜索,直到找到指定的.pc 文件为止。这样,库的头文件的搜索路径的设置实际上就变成了对.pc文件搜索路径的设置。
环境变量PKG_CONFIG_PATH设置方式如下:
#设置PKG_CONFIG_PATH
export PKG_CONFIG_PATH=/opt/gtk/lib/pkgconfig:$PKG_CONFIG_PATH
#检查PKG_CONFIG_PATH
echo $PKG_CONFIG_PATH
LIBRARY_PATH
LIBRARY_PATH环境变量用于在 程序编译期间 查找动态链接库时指定查找共享库的路径。
GCC编译、链接生成可执行文件时,动态库的搜索路径就包含LIBRARY_PATH,具体的搜索路径顺序如下(注意不会递归性地在其子目录下搜索)
- gcc编译、链接命令中的-L选项;
- gcc的环境变量的LIBRARY_PATH(多个路径用冒号分割);
- gcc默认动态库目录:/lib:/usr/lib:usr/lib64:/usr/local/lib。
环境变量LIBRARY_PATH设置方式如下:
#设置LIBRARY_PATH
export LIBRARY_PATH=/opt/gtk/lib:$LIBRARY_PATH
#检查LIBRARY_PATH
echo $LIBRARY_PATH
LD_LIBRARY_PATH
LD_LIBRARY_PATH环境变量用于在程序加载运行期间查找动态链接库时指定除了系统默认路径之外的其他路径。
链接生成二进制可执行文件后,运行该程序加载动态库文件时就会搜索包含LD_LIBRARY_PATH路径下的动态库,具体顺序如下:
- 编译目标代码时指定的动态库搜索路径:用选项-Wl,rpath和include指定的动态库的搜索路径,比如gcc -Wl,-rpath,include -L. -ldltest hello.c,在执行文件时会搜索路径
./include
; - 环境变量LD_LIBRARY_PATH(多个路径用冒号分割);
- 在 /etc/ld.so.conf.d/ 目录下的配置文件指定的动态库绝对路径(通过ldconfig生效,一般是非root用户时使用);
- gcc默认动态库目录:/lib:/usr/lib:usr/lib64:/usr/local/lib等。
由上面搜索逻辑可知对于处于默认库搜索路径之外的库,需要将库的位置添加到库的搜索路径之中。设置库文件的搜索路径有下列两种方式,可任选其一使用:
- 在环境变量LD_LIBRARY_PATH中指明库的搜索路径。
- 在/etc/ld.so.conf 文件中添加库的搜索路径。
环境变量LD_LIBRARY_PATH设置方式如下:
#设置LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/opt/gtk/lib:$LD_LIBRARY_PATH
#检查LD_LIBRARY_PATH
echo $LD_LIBRARY_PATH
需要注意的是:第二种搜索路径的设置方式对于程序连接时的库(包括共享库和静态库)的定位已经足够了,但是对于使用了共享库的程序的执行还是不够的。 这是因为为了加快程序执行时对共享库的定位速度,避免使用搜索路径查找共享库的低效率,所以是直接读取库列表文件 /etc/ld.so.cache 从中进行搜索的。
/etc/ld.so.cache是一个非文本的数据文件,不能直接编辑,它是根据/etc/ld.so.conf中设置的搜索路径由/sbin/ldconfig命令将这些搜索路径下的共享库文件集中在一起而生成的(ldconfig命令要以root权限执行)。
为了保证程序执行时对库的定位,在/etc/ld.so.conf中进行了库搜索路径的设置之后,还必须要运行如下命令更新/etc/ld.so.cache文件之后才可以。
/sbin/ldconfig
参考资料
- https://blog.csdn.net/ddreaming/article/details/53096411
- https://www.cnblogs.com/dongry/p/10511786.html
- https://www.cnblogs.com/oubo/archive/2011/12/06/2394631.html
- https://my.oschina.net/lxrm/blog/514723
- https://www.cnblogs.com/panfeng412/archive/2011/10/20/library_path-and-ld_library_path.html
- https://edonymu.com/2017/06/11/gcc中的环境变量library_path和ld_library_path/