帮我理解为什么我简单使用Python的ctypes模块失败

5 投票
4 回答
907 浏览
提问于 2025-04-16 02:34

我正在尝试理解Python中的“ctypes”模块。我做了一个简单的例子,理想情况下是用它来封装statvfs()这个函数调用。代码看起来是这样的:

from ctypes import *

class struct_statvfs (Structure):
    _fields_ = [
            ('f_bsize', c_ulong),
            ('f_frsize', c_ulong),
            ('f_blocks', c_ulong),
            ('f_bfree', c_ulong),
            ('f_bavail', c_ulong),
            ('f_files', c_ulong),
            ('f_ffree', c_ulong),
            ('f_favail', c_ulong),
            ('f_fsid', c_ulong),
            ('f_flag', c_ulong),
            ('f_namemax', c_ulong),
            ]


libc = CDLL('libc.so.6')
libc.statvfs.argtypes = [c_char_p, POINTER(struct_statvfs)]
s = struct_statvfs()

res = libc.statvfs('/etc', byref(s))
print 'return = %d, f_bsize = %d, f_blocks = %d, f_bfree = %d' % (
    res, s.f_bsize, s.f_blocks, s.f_bfree)

运行这个代码总是返回:

return = 0, f_bsize = 4096, f_blocks = 10079070, f_bfree = 5048834
*** glibc detected *** python: free(): invalid next size (fast): 0x0000000001e51780 ***
*** glibc detected *** python: malloc(): memory corruption (fast): 0x0000000001e517e0 ***

我找不到关于如何用复杂类型作为参数来调用函数的例子(有很多关于函数返回复杂类型的例子),但我盯着ctypes的文档看了一天,我觉得我的调用语法是正确的……而且它确实在调用statvfs()并且得到了正确的结果。

我是不是误解了ctypes的文档?还是这里发生了其他事情?

谢谢!

4 个回答

2

正如Eli所说,查看/usr/include/bits/statvfs.h文件,你的结构体定义得不太对。

在我的64位Gentoo系统上,它应该是这样的:

 class struct_statvfs (Structure):
    _fields_ = [
            ('f_bsize', c_ulong),
            ('f_frsize', c_ulong),
            ('f_blocks', c_ulong),
            ('f_bfree', c_ulong),
            ('f_bavail', c_ulong),
            ('f_files', c_ulong),
            ('f_ffree', c_ulong),
            ('f_favail', c_ulong),
            ('f_fsid', c_ulong),
            ('f_flag', c_ulong),
            ('f_namemax', c_ulong),
            ('__f_space', c_int * 6) # you are missing this
            ]
2

关于statvfs的手册说明,它提到使用的结构体大概是这样定义的,所以你不能完全依赖手册上列出的字段。

我猜在你定义的结构体后面可能还有其他字段。这会导致statvfs函数在你的结构体外部写入数据,从而覆盖了内存。我通过在我的结构体定义中的_fields_里添加一个很大的填充字段,解决了这个问题:

("padding", c_int * 1000),

需要注意的是,我的脚本表现出的问题和你的不太一样;我遇到了段错误,而你只是收到了错误信息。不过,我猜这可能是同样的问题,所以你可以试着添加一些填充字段,看看问题是否还存在。

4

执行这个命令可以在你的系统上获取struct statvfs的具体定义:

echo '#include <sys/statvfs.h>' | gcc -E - | less

然后按下/struct statvfs<enter>,这样可以直接跳到定义的地方,方便你查看。

另外,你也可以看看我对fusepy的补丁,还有他们的定义

撰写回答