帮我理解为什么我简单使用Python的ctypes模块失败
我正在尝试理解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的补丁,还有他们的定义。