Я написал оболочку 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__
, все равно происходит сбой).
Любая идея о том, как исправить это?
Вы установили неправильные типы возврата, и вы не установили каких-либо типов аргументов. Исправьте их и посмотрите, ведет ли результат по-разному. – user2357112
Какой тип возврата следует использовать? Я попробовал c_char_p, но потом у меня есть ошибка *** Ошибка в 'python3 ': munmap_chunk(): недействительный указатель: 0x00007fd25ec0da28 *** Согласно документации, c_char_p для NUL terminaded char *, что не так Вот. –
Цитирование [docs] (https://docs.python.org/2/library/ctypes.html#ctypes.c_char_p), «Для указателя общего символа, который также может указывать на двоичные данные,« POINTER (c_char) » должен быть использован.". – user2357112