【问题标题】:Should the order of import statements matter when importing a .so?导入 .so 时,导入语句的顺序是否重要?
【发布时间】:2013-08-11 21:51:16
【问题描述】:

尝试加载使用 boost python 编译的 python 模块时出现以下导入错误。

ImportError: /path/to/library/libxml2.so.2: symbol gzopen64, version ZLIB_1.2.3.3 not defined in file libz.so.1 with link time reference

奇怪的是,如果这是要导入的非标准模块,我看不到此错误。即,如果我先导入其他模块,然后再导入此模块,则会因导入错误而失败。不知道出了什么问题或如何调试。

编辑: 要准确显示问题:

$ python -c 'import json, libMYBOOST_PY_LIB' # DOES NOT WORK!!!
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ImportError: path/to/xml_library/libxml2.so: symbol gzopen64, version ZLIB_1.2.3.3 not defined in file libz.so.1 with link time reference
$ python -c 'import libMYBOOST_PY_LIB, json' # WORKS NOW!!!
$

它不仅仅是 json,在我的模块之前导入时,很少有其他模块也会导致同样的问题。例如。 urllib2

【问题讨论】:

  • 从 json 和 libMYBOOST_PY_LIB 导入 libz 是否存在版本冲突,由导入顺序解决(获取最新的)?
  • ldd /path/to/_json.so 没有 libz.so 作为依赖项。我检查对了吗?
  • 可能您需要展示如何编译和生成 libMYBOOST_PY_LIB,例如 g++ 命令。它可以提示您如何链接 libxml 库。
  • 正在做:ldd /usr/lib/x86_64-linux-gnu/libjson-glib-1.0.so.0.1502.0 在 Ubuntu 中显示 libz.so.1 => /lib/x86_64-linux- gnu/libz.so.1 (0x00007fd6f1892000)

标签: c++ python import boost-python shared-libraries


【解决方案1】:

import 语句的顺序很重要。

As documented in the python language reference:

一旦知道模块的名称(除非另有说明,术语“模块”将指包和模块),就可以开始搜索模块或包。第一个检查的地方是sys.modules,之前已经导入的所有模块的缓存。如果在那里找到该模块,则在导入的步骤 (2) 中使用它。

任何模块都可以改变:

他们也可以更改导入钩子:

导入挂钩可以让您从 zip 文件、任何类型的存档文件、网络等加载模块。


import libMYBOOST_PY_LIB

该语句肯定会修改sys.modules,将其依赖项加载到模块缓存中。它也可以修改sys.path。实际上,框架(例如boostzopedjangorequests...)附带电池/它们所依赖的模块的副本是很常见的。

  • django 附带json
  • requestsurllib3 一起发货

要准确查看库将加载的内容,您可以使用:

python -v -c 'import libMYBOOST_PY_LIB'

【讨论】:

    【解决方案2】:

    问题

    问题出在操作系统上。 Linux 库(动态链接的共享对象库)可以依赖于其他库(可以再次依赖于其他库等等)。如果这些依赖库未正确解析,则会出现您描述的错误。

    什么是共享库(so)

    您可以通过获取多个目标文件并将它们链接在一起来创建共享库。链接器在创建共享库时会保留大量元数据:

    1. 重定位表
    2. 导出符号列表(其他人可以访问的函数和变量)
    3. 导入符号列表(此库使用的其他库中的函数和变量)
    4. 其他库的文件名列表,可用于满足导入的符号

    当使用该库时,系统会加载该库,更改重定位表引用的地址,然后尝试查找导入的符号。对于这些,系统首先检查已加载的库。如果这不满足所有符号,它会尝试查找库中列出的文件名并检查是否存在具有该名称的文件,它是否是有效的库以及是否导出所需的符号。

    通常这里的“系统”是动态加载器,它运行在用户空间,而不是内核空间。

    如何检查程序使用了哪些库

    您可以使用推荐 ldd 来检查库的内容。

    如果您想检查正在运行的可执行文件,请尝试 lsof 并过滤 *.so 并检查 /proc/[pid]/maps

    如何调试您的问题

    在您的情况下,请在加载相关库之前直接保留程序(例如,从控制台插入读取或睡眠命令)。然后检查当前加载的库。您会发现,在好的情况下,已经加载了一个库来导出相关符号。在错误情况下,此库未加载,系统将在下一步尝试加载错误的依赖库(例如,缺少所需符号的库的不同版本)。

    库的顺序重要吗

    通常不会,但这取决于细节。当不同版本需要相同的库或系统在所有情况下都无法解析共享库时,它可能变得很重要。不幸的是,这些问题很难调试。在 Windows 上你有 DLL 地狱,在 Linux 上它与共享对象类似。祝您调试问题顺利!

    【讨论】:

      猜你喜欢
      • 2012-02-27
      • 2021-01-22
      • 1970-01-01
      • 2022-06-17
      • 2017-02-05
      • 2019-03-15
      • 1970-01-01
      • 1970-01-01
      • 2014-08-14
      相关资源
      最近更新 更多