2016-06-21 5 views
2

Я написал оболочку Python для библиотеки C, используя ctypes. Все работало нормально, пока я не попробовал его с модулем threading.Ошибка сегментации при использовании ctypes и threading

Вот небольшой пример, чтобы воспроизвести его:

foo.c

#include <stdlib.h> 
#include <assert.h> 

char *foo_init() { 
    char *result = (char*)malloc(sizeof(char)); 
    assert(result); 
    return result; 
} 

char foo_get(const char* p) { 
    return *p; 
} 

void foo_set(char *p, char value) { 
    *p = value; 
} 

void foo_del(char *p) { 
    free(p); 
} 

foo.py

import threading 
import ctypes 
libfoo=ctypes.CDLL('foo.so') 

libfoo.foo_init.restype = ctypes.c_void_p 
libfoo.foo_get.restype = ctypes.c_char 

class Foo: 
    def __init__(self): 
     self.__obj__ = libfoo.foo_init() 

    def get(self): 
     return libfoo.foo_get(self.__obj__) 

    def set(self, value): 
     libfoo.foo_set(self.__obj__, ctypes.c_char(value)) 

    def __del__(self): 
     libfoo.foo_del(self.__obj__) 

x = Foo() 
x.set(b'x') 
print(x.get()) 

def compute(): 
    y = Foo() 
    y.set(b'y') 
    print(y.get()) 

t = threading.Thread(target=compute) 
t.start() 
t.join() 

Компиляция:

gcc -Wall -shared -o foo.so -fPIC foo.c 

Исполнение:

export LD_LIBRARY_PATH=. 
python3 foo.py 

Результат:

b'x' 
[1] 8627 segmentation fault python3 foo.py 

Мы видим здесь, что x.get() правильно напечатано в то время как y.get() делает ошибку сегментации. Таким образом, кажется, что существует проблема, связанная с ctypes и threading.

Обратите внимание, что если я перемещаю инициализацию y вне функции compute, программа автоматически завершается.

Кроме того, каждая из функций, которые манипулируют указателем, вызывает ошибку сегментации (например, если я просто сохраняю функции __init__ и __del__, все равно происходит сбой).

Любая идея о том, как исправить это?

+1

Вы установили неправильные типы возврата, и вы не установили каких-либо типов аргументов. Исправьте их и посмотрите, ведет ли результат по-разному. – user2357112

+0

Какой тип возврата следует использовать? Я попробовал c_char_p, но потом у меня есть ошибка *** Ошибка в 'python3 ': munmap_chunk(): недействительный указатель: 0x00007fd25ec0da28 *** Согласно документации, c_char_p для NUL terminaded char *, что не так Вот. –

+0

Цитирование [docs] (https://docs.python.org/2/library/ctypes.html#ctypes.c_char_p), «Для указателя общего символа, который также может указывать на двоичные данные,« POINTER (c_char) » должен быть использован.". – user2357112

ответ

0

@ user2357112 дал мне ответ в комментариях: важно добавить (хороший) тип возврата и типы аргументов.

Замена начало foo.py следующим исправить проблему:

libfoo.foo_init.restype = ctypes.POINTER(ctypes.c_char) 
libfoo.foo_init.argtypes = [] 
libfoo.foo_get.restype = ctypes.c_char 
libfoo.foo_get.argtypes = [ctypes.POINTER(ctypes.c_char)] 
libfoo.foo_set.argtypes = [ctypes.POINTER(ctypes.c_char), ctypes.c_char] 
libfoo.foo_del.argtypes = [ctypes.POINTER(ctypes.c_char)]