为什么Python在系统路径上找不到东西



我在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(),它将尝试按以下顺序查找库:

  1. (仅ELF(如果调用对象(即从中调用dlopen((的共享库或可执行文件(包含DT_RPATH标记,并且不包含DT_RUNPATH标记,则DT_RPATH标记
  2. 如果在程序启动时,环境变量LD_LIBRARY_PATH被定义为包含冒号分隔的列表的目录,然后搜索这些目录。(作为一项安全措施变量对于设置用户ID和设置组ID程序被忽略。(o
  3. (仅限ELF(如果调用对象包含DT_RUNPATH标记,则会搜索该标记中列出的目录。o
  4. 检查缓存文件/etc/ld.so.cache(由ldconfig(8(维护(以查看它是否包含filename条目。o
  5. 将搜索目录/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

相关内容

  • 没有找到相关文章

最新更新