我在Linux容器中编译了yajl,它将编译的文件放在/usr/local/lib
中。我还将这个文件夹添加到了系统路径中,所以我认为我应该能够引用编译后的libyajl.so
文件,而无需指定该文件的完整路径。但当我尝试pip install yajl-py
时,它失败了,当它尝试运行这行代码时,它也失败了:
cdll.LoadLibrary('libyajl.so')
我得到错误:
OSError:libyajl.so:无法打开共享对象文件:没有这样的文件或目录
为了确认我设置正确,我打印路径:
import os
print(os.environ["PATH"])
我得到:
/usr/local/bin:/usr/local/sbin:/usr/sbin:/usr/bin:/usr\local/lib
为了确认如果找到文件,LoadLibrary
函数将工作,我运行:
from ctypes import cdll
cdll.LoadLibrary('/usr/local/lib/libyajl.so')
我得到了预期:
<CDLL"/usr/local/lib/libyajl.so",在0x7f61ded49ed0>
我有点不明白这里出了什么问题。有什么想法吗?
简短说明
简短的答案是,因为您使用的是Linux发行版-使用…重建您的/etc/ld.so.cache
。。。
sudo ldconfig
然后再次尝试使用ctypes.cdll.LoadLibrary
的代码,因为/usr/local/lib
中的库通常是由ctypes
索引和发现的。
更长的解释
从根本上讲,ctypes.cdll.LoadLibrary
使用系统调用dlopen()
,它将尝试按以下顺序查找库:
- (仅ELF(如果调用对象(即从中调用dlopen((的共享库或可执行文件(包含DT_RPATH标记,并且不包含DT_RUNPATH标记,则DT_RPATH标记
- 如果在程序启动时,环境变量LD_LIBRARY_PATH被定义为包含冒号分隔的列表的目录,然后搜索这些目录。(作为一项安全措施变量对于设置用户ID和设置组ID程序被忽略。(o
- (仅限ELF(如果调用对象包含DT_RUNPATH标记,则会搜索该标记中列出的目录。o
- 检查缓存文件/etc/ld.so.cache(由ldconfig(8(维护(以查看它是否包含filename条目。o
- 将搜索目录/lib和/usr/lib(按顺序(
因此,您可以看到要搜索的是LD_LIBRARY_PATH
,而不是PATH
。
但是,您的问题可能不是没有配置LD_LIBRARY_PATH
,因为dlopen
还会查找/etc/ld.so.cache
,这是一个特殊文件,是系统上已知共享对象的缓存,在我所知的任何典型Linux发行版上,/usr/local/lib
中的共享库都包含在该缓存中。
如何重建此缓存是通过工具ldconfig
实现的,该工具(通常(使用文件/etc/ld.so.conf
进行配置。在我的测试机器上(一个原始的DebianLinux安装(,/etc/ld.so.conf
看起来像这样:
# Multiarch support
/usr/local/lib/aarch64-linux-gnu
/lib/aarch64-linux-gnu
/usr/lib/aarch64-linux-gnu
/usr/lib/aarch64-linux-gnu/libfakeroot
# libc default configuration
/usr/local/lib
因此,您可以看到ld.so.cache
将从该目录中的库中编译。
所以,让我来演示一下在一个原始的Linux安装上会发生什么:
# docker run -it debian
apt update && apt install cmake git python3 python3-pip
git clone https://github.com/lloyd/yajl
cd yajl
./configure && make && make install
# build stuff
...
Install the project...
-- Install configuration: "Release"
-- Installing: /usr/local/lib/libyajl.so.2.1.1
-- Installing: /usr/local/lib/libyajl.so.2
-- Installing: /usr/local/lib/libyajl.so
-- Installing: /usr/local/lib/libyajl_s.a
-- Installing: /usr/local/include/yajl/yajl_parse.h
-- Installing: /usr/local/include/yajl/yajl_gen.h
-- Installing: /usr/local/include/yajl/yajl_common.h
-- Installing: /usr/local/include/yajl/yajl_tree.h
-- Installing: /usr/local/include/yajl/yajl_version.h
-- Installing: /usr/local/share/pkgconfig/yajl.pc
-- Installing: /usr/local/bin/json_reformat
-- Set runtime path of "/usr/local/bin/json_reformat" to ""
-- Installing: /usr/local/bin/json_verify
-- Set runtime path of "/usr/local/bin/json_verify" to ""
make[1]: Leaving directory '/root/yajl/build'
所以现在我有了/usr/local/lib/libyajl.so
(以及其他一些(,但Python找不到它:
root@11f76d842b80:~/yajl# python3
Python 3.9.2 (default, Feb 28 2021, 17:03:44)
[GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import ctypes
>>> ctypes.cdll.LoadLibrary("libyajl.so")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.9/ctypes/__init__.py", line 452, in LoadLibrary
return self._dlltype(name)
File "/usr/lib/python3.9/ctypes/__init__.py", line 374, in __init__
self._handle = _dlopen(self._name, mode)
OSError: libyajl.so: cannot open shared object file: No such file or directory
所以,您现在可以做两件事中的一件——一件是在运行python3
之前手动设置LD_LIBRARY_PATH
,就像这样…
root@11f76d842b80:~/yajl# LD_LIBRARY_PATH="/usr/local/lib" python3
Python 3.9.2 (default, Feb 28 2021, 17:03:44)
[GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import ctypes
>>> ctypes.cdll.LoadLibrary("libyajl.so")
<CDLL 'libyajl.so', handle 2c32ba0 at 0xffffa1699910>
这使ctypes
能够找到库。
然而,当你也可以重建你的ld.so.cache
时,为什么要这样做?这通常是在系统重新启动(或其他事件,不确定每个发行版的作用(时重建的,但您也可以通过运行ldconfig
手动强制执行。
所以在我的Debian容器中,ctypes
还找不到libyajl.so
,我这样做。。。
# you might need 'sudo ldconfig'
root@11f76d842b80:~/yajl# ldconfig
root@11f76d842b80:~/yajl# python3
Python 3.9.2 (default, Feb 28 2021, 17:03:44)
[GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import ctypes
>>> ctypes.cdll.LoadLibrary("libyajl.so")
<CDLL 'libyajl.so', handle 4040a30 at 0xffffa0d24ee0>
现在一切都好了,libyajl.so
已经被索引了。
PATH
只用于查找可执行文件,而不是库。
要指定库的其他搜索路径,请使用LD_LIBRARY_PATH
。