- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我正在一个非常古老的 Red Hat 系统上编写一个 Python C 扩展。系统有 zlib 1.2.3,它不能正确支持大文件。不幸的是,我不能只将系统 zlib 升级到更新的版本,因为一些包会进入内部 zlib 结构并且在更新的 zlib 版本上会中断。
我想构建我的扩展,以便所有 zlib 调用(gzopen()
、gzseek()
等)都解析为我安装的自定义 zlib在我的用户目录中,而不影响其余的 Python 可执行文件和其他扩展。
我已经尝试通过在链接期间将 libz.a
添加到 gcc 命令行来在 libz.a
中进行静态链接,但是它不起作用(仍然无法创建大文件例如使用 gzopen()
)。我还尝试将 -z origin -Wl,-rpath=/path/to/zlib -lz
传递给 gcc,但这也没有用。
因为新版本的 zlib 仍然命名为 zlib 1.x
,soname
是相同的,所以我认为符号版本控制不会工作。有没有办法做我想做的事?
我在 32 位 Linux 系统上。 Python版本为2.6,是定制的。
编辑:
我创建了一个最小示例。我正在使用 Cython(版本 0.19.1)。
文件gztest.pyx
:
from libc.stdio cimport printf, fprintf, stderr
from libc.string cimport strerror
from libc.errno cimport errno
from libc.stdint cimport int64_t
cdef extern from "zlib.h":
ctypedef void *gzFile
ctypedef int64_t z_off_t
int gzclose(gzFile fp)
gzFile gzopen(char *path, char *mode)
int gzread(gzFile fp, void *buf, unsigned int n)
char *gzerror(gzFile fp, int *errnum)
cdef void print_error(void *gzfp):
cdef int errnum = 0
cdef const char *s = gzerror(gzfp, &errnum)
fprintf(stderr, "error (%d): %s (%d: %s)\n", errno, strerror(errno), errnum, s)
cdef class GzFile:
cdef gzFile fp
cdef char *path
def __init__(self, path, mode='rb'):
self.path = path
self.fp = gzopen(path, mode)
if self.fp == NULL:
raise IOError('%s: %s' % (path, strerror(errno)))
cdef int read(self, void *buf, unsigned int n):
cdef int r = gzread(self.fp, buf, n)
if r <= 0:
print_error(self.fp)
return r
cdef int close(self):
cdef int r = gzclose(self.fp)
return 0
def read_test():
cdef GzFile ifp = GzFile('foo.gz')
cdef char buf[8192]
cdef int i, j
cdef int n
errno = 0
for 0 <= i < 0x200:
for 0 <= j < 0x210:
n = ifp.read(buf, sizeof(buf))
if n <= 0:
break
if n <= 0:
break
printf('%lld\n', <long long>ifp.tell())
printf('%lld\n', <long long>ifp.tell())
ifp.close()
文件setup.py
:
import sys
import os
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
if __name__ == '__main__':
if 'CUSTOM_GZ' in os.environ:
d = {
'include_dirs': ['/home/alok/zlib_lfs/include'],
'extra_objects': ['/home/alok/zlib_lfs/lib/libz.a'],
'extra_compile_args': ['-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -g3 -ggdb']
}
else:
d = {'libraries': ['z']}
ext = Extension('gztest', sources=['gztest.pyx'], **d)
setup(name='gztest', cmdclass={'build_ext': build_ext}, ext_modules=[ext])
我的自定义 zlib
位于 /home/alok/zlib_lfs
(zlib 版本 1.2.8):
$ ls ~/zlib_lfs/lib/
libz.a libz.so libz.so.1 libz.so.1.2.8 pkgconfig
要使用此 libz.a
编译模块:
$ CUSTOM_GZ=1 python setup.py build_ext --inplace
running build_ext
cythoning gztest.pyx to gztest.c
building 'gztest' extension
gcc -fno-strict-aliasing -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/alok/zlib_lfs/include -I/opt/include/python2.6 -c gztest.c -o build/temp.linux-x86_64-2.6/gztest.o -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -g3 -ggdb
gcc -shared build/temp.linux-x86_64-2.6/gztest.o /home/alok/zlib_lfs/lib/libz.a -L/opt/lib -lpython2.6 -o /home/alok/gztest.so
gcc
正在传递我想要的所有标志(添加到 libz.a
的完整路径、大文件标志等)。
要在没有自定义 zlib 的情况下构建扩展,我可以在没有定义 CUSTOM_GZ
的情况下进行编译:
$ python setup.py build_ext --inplace
running build_ext
cythoning gztest.pyx to gztest.c
building 'gztest' extension
gcc -fno-strict-aliasing -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/opt/include/python2.6 -c gztest.c -o build/temp.linux-x86_64-2.6/gztest.o
gcc -shared build/temp.linux-x86_64-2.6/gztest.o -L/opt/lib -lz -lpython2.6 -o /home/alok/gztest.so
我们可以检查gztest.so
文件的大小:
$ stat --format='%s %n' original/gztest.so custom/gztest.so
62398 original/gztest.so
627744 custom/gztest.so
因此,正如预期的那样,静态链接文件要大得多。
我现在可以:
>>> import gztest
>>> gztest.read_test()
然后它将尝试读取当前目录中的foo.gz
。
当我使用非静态链接的 gztest.so
执行此操作时,它按预期工作,直到它尝试读取超过 2 GB。
当我使用静态链接的 gztest.so
执行此操作时,它会转储核心:
$ python -c 'import gztest; gztest.read_test()'
error (2): No such file or directory (0: )
0
Segmentation fault (core dumped)
关于 No such file or directory
的错误具有误导性——该文件存在并且 gzopen()
实际上成功返回。 gzread()
虽然失败了。
这是 gdb
回溯:
(gdb) bt
#0 0xf730eae4 in free () from /lib/libc.so.6
#1 0xf70725e2 in ?? () from /lib/libz.so.1
#2 0xf6ce9c70 in __pyx_f_6gztest_6GzFile_close (__pyx_v_self=0xf6f75278) at gztest.c:1140
#3 0xf6cea289 in __pyx_pf_6gztest_2read_test (__pyx_self=<optimized out>) at gztest.c:1526
#4 __pyx_pw_6gztest_3read_test (__pyx_self=0x0, unused=0x0) at gztest.c:1379
#5 0xf769910d in call_function (oparg=<optimized out>, pp_stack=<optimized out>) at Python/ceval.c:3690
#6 PyEval_EvalFrameEx (f=0x8115c64, throwflag=0) at Python/ceval.c:2389
#7 0xf769a3b4 in PyEval_EvalCodeEx (co=0xf6faada0, globals=0xf6ff81c4, locals=0xf6ff81c4, args=0x0, argcount=0, kws=0x0, kwcount=0, defs=0x0, defcount=0, closure=0x0) at Python/ceval.c:2968
#8 0xf769a433 in PyEval_EvalCode (co=0xf6faada0, globals=0xf6ff81c4, locals=0xf6ff81c4) at Python/ceval.c:522
#9 0xf76bbe1a in run_mod (arena=<optimized out>, flags=<optimized out>, locals=<optimized out>, globals=<optimized out>, filename=<optimized out>, mod=<optimized out>) at Python/pythonrun.c:1335
#10 PyRun_StringFlags (str=0x80a24c0 "import gztest; gztest.read_test()\n", start=257, globals=0xf6ff81c4, locals=0xf6ff81c4, flags=0xffbf2888) at Python/pythonrun.c:1298
#11 0xf76bd003 in PyRun_SimpleStringFlags (command=0x80a24c0 "import gztest; gztest.read_test()\n", flags=0xffbf2888) at Python/pythonrun.c:957
#12 0xf76ca1b9 in Py_Main (argc=1, argv=0xffbf2954) at Modules/main.c:548
#13 0x080485b2 in main ()
其中一个问题似乎是回溯中的第二行引用了libz.so.1
!如果我执行 ldd gztest.so
,除其他行外,我得到:
libz.so.1 => /lib/libz.so.1 (0xf6f87000)
虽然我不确定为什么会这样。
编辑 2:
我最终做了以下事情:
z_
前缀导出的所有符号编译了我的自定义 zlib。 zlib
的 configure
脚本让这一切变得非常简单:只需运行 ./configure --zprefix ...
.gzopen64()
而不是 gzopen()
。这是因为我想确保我使用的是正确的“底层”代码。z_off64_t
。zlib.a
静态链接到 Cython 生成的共享库中。我在与 gcc 链接时使用了 '-Wl,--whole-archive/home/alok/zlib_lfs_z/lib/libz.a -Wl,--no-whole-archive'
来实现这一点。可能有其他方法,也可能不需要,但这似乎是确保使用正确库的最简单方法。通过上述更改,大文件可以工作,而其余 Python 扩展模块/进程可以像以前一样工作。
最佳答案
看起来这与 another question 中的问题类似, 除了我得到相反的行为。
我下载了 zlib-1.2.8
压缩包,运行 ./configure
,然后更改了以下 Makefile
变量...
CFLAGS=-O3 -fPIC -D_LARGEFILE64_SOURCE=1 -D_FILE_OFFSET_BITS=64
SFLAGS=-O3 -fPIC -D_LARGEFILE64_SOURCE=1 -D_FILE_OFFSET_BITS=64
...主要是将 -fPIC
添加到 libz.a
以便我可以在共享库中链接到它。
然后我在 gzlib.c
函数中添加了一些 printf()
语句 gzopen()
, gzopen64()
和 gz_open()
,这样我就可以很容易地判断它们是否被调用了。
在构建libz.a
和libz.so
之后,我创建了一个非常简单的foo.c
...
#include "zlib-1.2.8/zlib.h"
void main()
{
gzFile foo = gzopen("foo.gz", "rb");
}
...并编译了一个 foo
独立二进制文件和一个 foo.so
共享库...
gcc -fPIC -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -o foo.o -c foo.c
gcc -o foo foo.o zlib-1.2.8/libz.a
gcc -shared -o foo.so foo.o zlib-1.2.8/libz.a
运行 foo
按预期工作,并打印...
gzopen64
gz_open
...但是在 Python 中使用 foo.so
...
import ctypes
foo = ctypes.CDLL('./foo.so')
foo.main()
...没有打印任何东西,所以我猜它正在使用 Python 的 libz.so
...
$ ldd `which python`
...
libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f5af2c68000)
...
...即使 foo.so
不使用它...
$ ldd foo.so
linux-vdso.so.1 => (0x00007fff93600000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc8bfa98000)
/lib64/ld-linux-x86-64.so.2 (0x00007fc8c0078000)
我让它工作的唯一方法是直接用...打开自定义 libz.so
import ctypes
libz = ctypes.CDLL('zlib-1.2.8/libz.so.1.2.8')
libz.gzopen64('foo.gz', 'rb')
...打印出来...
gzopen64
gz_open
请注意,从 gzopen
到 gzopen64
的转换是由预处理器完成的,因此我不得不直接调用 gzopen64()
。
这是修复它的一种方法,但更好的方法可能是重新编译自定义 Python 2.6 以链接到静态 zlib-1.2.8/libz.a
,或禁用 zlibmodule.c
完全,那么您的链接选项将具有更大的灵 active 。
更新
关于 _LARGEFILE_SOURCE
与 _LARGEFILE64_SOURCE
:我之所以指出这一点,是因为 zlib.h
中的评论...
/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or
* change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if
* both are true, the application gets the *64 functions, and the regular
* functions are changed to 64 bits) -- in case these are set on systems
* without large file support, _LFS64_LARGEFILE must also be true
*/
...这意味着如果您不定义 _LARGEFILE64_SOURCE
,则不会公开 gzopen64()
函数。我不确定 _LFS64_LARGEFILE
是否适用于您的系统。
关于带有自定义共享库的 Python C 扩展链接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16841674/
我想使用 li 和 ul 制作一个多级下拉列表,以便显示我博客中按年和月排序的所有文章。我希望我的下拉菜单看起来像 Google Blogspot 下拉菜单: 这是我的 CSS 和 HTML 代码 u
我在 Win 7 64 机器上将 CodeBlocks 与 gcc 4.7.2 和 gmp 5.0.5 结合使用。开始使用 gmpxx 后,我看到一个奇怪的段错误,它不会出现在 +、- 等运算符中,但
我正在使用 tern 为使用 CodeMirror 运行的窗口提供一些增强的智能感知,它工作正常,但我遇到了一个问题,我想添加一些自定义“types”,可以这么说,这样下拉列表中它们旁边就有图标了。我
我正在尝试让我的 PC 成为 Android 2.3.4 设备的 USB 主机,以便能够在不需要实际“附件”的情况下开发 API。为此,我需要将 PC 设置为 USB 主机和“设备”(在我的例子中是运
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 9
我在设置服务器方面几乎是个新手,但遇到了一个问题。我有一个 Ubuntu 16.04 VPS 并安装了 Apache2 和 Tomcat7。我正在为 SSL 使用 LetsEncrypt 和 Cert
我在一个基于谷歌地图的项目上工作了超过 6 个月。我使用的是 Google Maps API V1 及其开发人员 API key 。当我尝试发布应用程序时,我了解到 Google API V1 已被弃
我是 Python 的新手,所以如果我对一些简单的事情感到困惑,请原谅。 我有一个这样的对象: class myObject(object): def __init__(self):
这个问题已经有答案了: How can I access object properties containing special characters? (2 个回答) 已关闭 9 年前。 我正在尝
我有下面的 CSS。我想要的是一种流体/液体(因为缺乏正确的术语)css。我正在为移动设备开发,当我改变模式时 从纵向 View 到陆地 View ,我希望它流畅。现在的图像 在陆地 View 中效
我正在尝试使用可以接受参数的缓存属性装饰器。 我查看了这个实现:http://www.daniweb.com/software-development/python/code/217241/a-cac
这个问题在这里已经有了答案: Understanding slicing (36 个答案) 关闭 6 年前。 以a = [1,2,3,4,5]为例。根据我的直觉,我认为 a[::-1] 与 a[0:
mysqldump -t -u root -p mytestdb mytable --where=datetime LIKE '2014-09%' 这就是我正在做的事情,它会返回: mysqldum
我正在制作销售税计算器,除了总支付金额部分外,其他一切都正常。在我的程序中,我希望能够输入一个数字并获得该项目的税额我还希望能够获得支付的总金额,包括交易中的税金。到目前为止,我编写的代码完成了所有这
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许在 Stack Overflow 上提出有关通用计算硬件和软件的问题。您可以编辑问题,使其成为
我是否必须进行任何额外的设置才能让 apache-airflow 在任务失败时向我发送电子邮件。我的配置文件中有以下内容(与默认值保持不变): [email] email_backend = airf
这个问题在这里已经有了答案: What does the $ symbol do in VBA? (5 个回答) 3年前关闭。 使用返回字符串(如 Left)的内置函数有什么区别吗?或使用与 $ 相同
我有一个用VB6编写的应用程序,我需要使用一个用.NET编写的库。有什么方法可以在我的应用程序上使用该库吗? 谢谢 最佳答案 这取决于。您可以控制.NET库吗? 如果是这样,则可以修改您的库,以便可以
当我创建一个以 ^ 开头的类方法时,我尝试调用它,它给了我一个错误。 class C { method ^test () { "Hi" } } dd C.new.test; Too m
我已经使用 bower 安装了 angularjs 和 materialjs。 凉亭安装 Angular Material 并将“ngMaterial”注入(inject)我的应用程序,但出现此错误。
我是一名优秀的程序员,十分优秀!