Помогите мне понять, почему мое тривиальное использование модуля Python ctypes не удается - PullRequest
5 голосов
/ 10 августа 2010

Я пытаюсь понять модуль 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 ]

4 голосов
/ 10 августа 2010

Выполните эту команду, чтобы получить точное определение struct statvfs в вашей системе:

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

Затем нажмите /struct statvfs<enter>, чтобы перейти к определению и просмотреть оттуда.

Также взгляните на мой патч на fusepy и их определение .

2 голосов
/ 10 августа 2010

Как указывает 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 голосов
/ 10 августа 2010

Страница man для statvfs гласит, что используемая им структура «определена примерно следующим образом», поэтому вы не можете обязательно принимать списки полей manpage как завершенные.

Я предполагаю, что есть дополнительные поля структуры после конца структуры, как вы ее определили. Это заставляет функцию statvfs перезаписывать память вне вашей структуры. Я решил эту проблему, добавив огромное поле заполнения к _fields_ в моем определении структуры:

("padding", c_int * 1000),

Имейте в виду, что мой сценарий проявил проблему не так, как ваш; Я получил segfault, тогда как вы просто получили несколько сообщений об ошибках. Тем не менее, я предполагаю, что это та же самая проблема, поэтому вы должны попробовать добавить некоторые отступы и посмотреть, сохраняется ли проблема.

0 голосов
/ 10 августа 2010

В соответствии с успешными оболочками pyfuse ctypes для Fuse, для struct statvfs в Linux используется следующее:

class c_statvfs(Structure):
    _fields_ = [
        ('f_bsize', c_ulong),
        ('f_frsize', c_ulong),
        ('f_blocks', c_fsblkcnt_t),
        ('f_bfree', c_fsblkcnt_t),
        ('f_bavail', c_fsblkcnt_t),
        ('f_files', c_fsfilcnt_t),
        ('f_ffree', c_fsfilcnt_t),
        ('f_favail', c_fsfilcnt_t)]

Подробнее: http://code.google.com/p/fusepy/

...