1.LIBRARY_PATH和LD_LIBRARY_PATH区别

参考:
LIBRARY_PATH和LD_LIBRARY_PATH的区别
LINUX中PATH,LIBRARY_PATH,LD_LIBRARY_PATH等环境变量的含义
LD_LIBRARY_PATH vs LIBRARY_PATH

1.1.两个环境变量的不同

gcc在编译链接时链接的动态库跟LIBRARY_PATH有关而跟LD_LIBRARY_PATH没关系!

LIBRARY_PATHLD_LIBRARY_PATH是Linux下的两个环境变量,二者的含义和作用分别如下:

  • LIBRARY_PATH环境变量用于在程序编译期间查找动态链接库时指定查找共享库的路径,例如,指定gcc编译需要用到的动态链接库的目录。设置方法如下(其中,LIBDIR1LIBDIR2为两个库目录):
export LIBRARY_PATH=LIBDIR1:LIBDIR2:$LIBRARY_PATH
  • LD_LIBRARY_PATH环境变量用于在程序加载运行期间查找动态链接库时指定除了系统默认路径之外的其他路径。注意,LD_LIBRARY_PATH中指定的路径会在系统默认路径之前进行查找。设置方法如下(其中,LIBDIR1LIBDIR2为两个库目录):
export LD_LIBRARY_PATH=LIBDIR1:LIBDIR2:$LD_LIBRARY_PATH

1.2.使用场景

举个例子,我们开发一个程序,经常会需要使用某个或某些动态链接库,为了保证程序的可移植性,可以先将这些编译好的动态链接库放在自己指定的目录下,然后按照上述方式将这些目录加入到LD_LIBRARY_PATH环境变量中,这样自己的程序就可以动态链接后加载库文件运行了。

区别与使用

  • 开发时(编译程序的时候):设置LIBRARY_PATH,以便gcc能够找到编译时需要的动态链接库,这样可以顺利通过编译链接生成可执行文件。但是注意:如果这个时候这个库不在LD_LIBRARY_PATH目录里,或者不在系统默认的动态库链接目录里,那么程序仍然是不能运行的,因为它运行到时候找不到链接的库,就无法链接这个库。

  • 发布时(程序编译完成,需要运行):设置LD_LIBRARY_PATH,以便程序加载运行时能够自动找到需要的动态链接库。

2.配合CMake实验测试

2.0.文件结构

在文件夹~/test下的目录结构:
在这里插入图片描述

2.1.测试1:LIBRARY_PATH包含库目录,LD_LIBRARY_PATH不包含

  • 首先在~/.zshrc中加入环境变量:export LIBRARY_PATH=~/test/Lib:$LIBRARY_PATH。然后重新打开一个窗口,输入printenv | grep LIBRARY查看一下是否加入了环境变量
  • 编写CMakeLists.txt文件如下
cmake_minimum_required(VERSION 3.0)
project(main)

add_executable(main main.cpp)
target_link_libraries(main libmyLog.so )
  • cmake … , make测试结果:可以正常编译链接得到可执行文件,但是运行时报错error while loading shared libraries: libmyLog.so: cannot open shared object file: No such file or directory

以上测试说明,LIBRARY_PATH包含库目录但LD_LIBRARY_PATH不包含的情况,可以正常编译链接到库,但是运行的时候链接不到库。

2.2.测试2:LIBRARY_PATH不包含库目录,LD_LIBRARY_PATH包含库目录

测试步骤仍然同上,注意编译之前重开窗口,然后printenv | grep LIBRARY查看一下是否真正满足 “LIBRARY_PATH不包含库目录但LD_LIBRARY_PATH包含库目录” 的要求。

测试结果是编译链接的时候就失败,报错/usr/bin/ld: 找不到 -lmyLog,也就是编译的时候就找不到库目录。

2.3.测试3:LIBRARY_PATH包含库目录,LD_LIBRARY_PATH包含库目录

测试步骤仍然同上,注意编译之前重开窗口,然后printenv | grep LIBRARY查看一下是否真正满足 “LIBRARY_PATH包含库目录且LD_LIBRARY_PATH也包含库目录” 的要求。

测试结果是编译链接通过,程序也可以正常运行。

2.4.测试4:和LD_LIBRARY_PATH同等作用的/etc/ld.so.conf

2.4.1./etc/ld.so.conf详解

参考:/etc/ld.so.conf详解

/etc/ld.so.confLD_LIBRARY_PATH具有同等的作用,都是负责存储程序运行时链接的动态库目录,而不是程序编译时链接的动态库目录。他们不同的地方在于前者是系统目录下的,属于默认路径(超级用户也可以更改);而后者是环境变量,用户可以在其中增加路径。因此程序运行时链接库的时候,优先链接后者,即环境变量LD_LIBRARY_PATH目录下的库。如果不能满足一些共享库相关性要求,则转回到 /etc/ld.so.conf 中指定的库。

首先查看/etc/ld.so*相关的文件如下图所示。
在这里插入图片描述

  • ld.so.conf文件和ld.so.conf.d文件夹下的文件是用户使用的。ld.so.conf是文件,它里面默认只有一句话是include /etc/ld.so.conf.d/*.conf。即ld.so.conf.d是一个文件夹,它里面还有很多.conf文件,真正的库包含目录就在这些文件中。然后顶层的ld.so.conf文件包含ld.so.conf.d下的所有文件,也就是统计了这些文件中的库目录。

  • ld.so.cache是真正和链接器打交道的。这个文件中的内容是系统生成的缓存文件,链接器到这个文件中读取链接的库。这个文件需要调用ldconfig命令来生成,或者刷新缓存。

因此,添加把库文件的路径加入到 /etc/ld.so.conf 的方法很简单,有两种方法:

  • 将库文件的绝对路径直接写进 /etc/ld.so.conf ,一行一个,例如:
include /etc/ld.so.conf.d/*.conf   # 系统默认路径
/home/cc/test/Lib  # 用户新增库路劲
  • /etc/ld.so.conf .d文件夹下新建一个自己的.conf文件,在里面写库目录,也是一行一个,例如/etc/ld.so.conf .d/mylib.conf文件内容:
/home/cc/test/Lib  # 用户新增库路劲

如下图可以看到,除了mylib.conf是我们自己新建的,其他都是安装软件的时候或者系统默认就有的。其中有x86_64-linux-gnu.conf,这个就是系统默认库包含目录中有/usr/lib/x86_64_linux-gnu文件夹下的库的根本所在!
在这里插入图片描述

库增加结束,调用sudo ldconfig命令更新缓存,当 ldconfig 操作结束时,会有一个最新的 /etc/ld.so.cache 文件,它反映对 /etc/ld.so.conf 所做的更改。从这一刻起,动态装入器在寻找共享库时会查看在 /etc/ld.so.conf 中指定的所有新目录。

最后查看我们增加的库, 要查看 ldconfig 可以“看到”的所有共享库,请输入ldconfig -p | less。这里用管道查找特定的路,输入ldconfig -p | grep libmyLog
在这里插入图片描述

2.4.2./etc/ld.so.conf测试

ld.so.conf添加自己的库,LIBRARY_PATH中没有库目录,此时编译链接不通过,报错同2.2。

ld.so.conf不添加自己的库,LIBRARY_PATH中有库目录,此时编译链接通过,但运行链接错误,报错同2.1。

ld.so.conf添加自己的库,LIBRARY_PATH中有库目录,此时编译链接通过,运行链接也无错误。

2.5.测试5:删除环境变量包含目录,使用cmake的find_library命令

CMakeLists.txt文件如下:

cmake_minimum_required(VERSION 3.0)
project(main)

find_library(MYLOG libmyLog.so "~/test/Lib")

if( MYLOG)   
     message(STATUS "MYLOG library DIR =  ${MYLOG} ")  
else()
     message(FATAL_ERROR "MYLOG library not found")
endif()

add_executable(main main.cpp)
target_link_libraries(main $[MYLOG} )   # 一定注意LOG是变量,所以要用${}引用!  

测试步骤仍然同上,注意编译之前重开窗口,然后printenv | grep LIBRARY查看一下是否真正满足 “LIBRARY_PATH不包含库目录且LD_LIBRARY_PATH也不包含库目录” 的要求。

测试结果是编译链接通过,程序也可以正常运行。

2.6.测试6:删除环境变量包含目录,使用cmake的link_directories命令

CMakeLists.txt文件如下:

cmake_minimum_required(VERSION 3.0)
project(main)

link_directories("~/test/Lib")  
add_executable(main main.cpp)
target_link_libraries(main libmyLog.so ) 

测试步骤仍然同上,注意编译之前重开窗口,然后printenv | grep LIBRARY查看一下是否真正满足 “LIBRARY_PATH不包含库目录且LD_LIBRARY_PATH也不包含库目录” 的要求。

测试结果是编译链接通过,程序也可以正常运行。

问题:之前一个工程遇到了使用link_directories加入库目录,但是编译链接的时候就是通过不了。最后在target_link_libraries中指定这个库的绝对路径才能编译链接上。很奇怪!

3.总结

如果链接到一个非系统默认或环境变量中的库,该如何写?

使用find_library,这样会编译时链接库的目录和运行时链接库的目录。否则就需要在LIBRARY_PATHLD_LIBRARY_PATH都加入库目录,这样才能保证编译链接成功、运行链接成功。

关于link_directories命令,之前使用遇到奇怪的问题,另外CMake官方也建议不要使用link_directories,而应该使用find_library

4.问题

  1. 既然ld.so.conf里的库等效于LD_LIBRARY_PATH,也就是程序运行时链接的库。那么程序编译时默认链接的库,即等效于LIBRARY_PATH的库路径是在哪里定义的呢?并没有找到!

  2. 关于link_directories命令,之前使用遇到奇怪的问题,另外CMake官方也建议不要使用link_directories,而应该使用find_library

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐