导入.so时导入语句的顺序是否重要?

时间:2013-08-09 21:00:27

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

我在尝试加载使用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

2 个答案:

答案 0 :(得分:5)

import陈述的顺序很重要。

As documented in the python language reference

  

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

任何模块都可以更改:

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

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


import libMYBOOST_PY_LIB

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

  • django附带json
  • requests附带urllib3

要确切了解库的加载方式,您可以使用:

python -v -c 'import libMYBOOST_PY_LIB'

答案 1 :(得分:3)

问题

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

什么是共享库(so)

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

  1. 重定位表
  2. 导出符号列表(其他人可以访问的函数和变量)
  3. 导入符号列表(此库从其他库中使用的函数和变量)
  4. 其他库的文件名列表,可用于满足导入的符号
  5. 使用库时,系统会加载库,更改重定位表引用的地址,然后尝试查找导入的符号。对于这些系统,系统首先检查已加载的库。如果这不满足所有符号,它会尝试查找库中列出的文件名,并检查是否存在具有该名称的文件,是否为有效库以及是否导出所需的符号。

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

    如何检查程序使用的库

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

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

    如何调试您的问题

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

    库的顺序是否重要

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