当我从ctypes调用的c函数实例化python对象时,我的嵌入式Python 3.3程序会出现段错误。
设置解释器后,我可以从c main成功实例化python Int(以及自定义c扩展类型):
#import <Python/Python.h>
#define LOGPY(x) \
{ fprintf(stderr, "%s: ", #x); PyObject_Print((PyObject*)(x), stderr, 0); fputc('\n', stderr); }
// c function to be called from python script via ctypes.
void instantiate() {
PyObject* instance = PyObject_CallObject((PyObject*)&PyLong_Type, NULL);
LOGPY(instance);
}
int main(int argc, char* argv[]) {
Py_Initialize();
instantiate(); // works fine
// run a script that calls instantiate() via ctypes.
FILE* scriptFile = fopen("emb.py", "r");
if (!scriptFile) {
fprintf(stderr, "ERROR: cannot open script file\n");
return 1;
}
PyRun_SimpleFileEx(scriptFile, scriptPath, 1); // close on completion
return 0;
}
然后我使用PyRun_SimpleFileEx运行python脚本。它看起来运行得很好,但是当它通过ctypes调用instantiate()时,程序会在PyObject_CallObject中进行段错误:
import ctypes as ct
dy = ct.CDLL('./emb')
dy.instantiate() # segfaults
lldb输出:
instance: 0
Process 52068 stopped
* thread #1: tid = 0x1c03, 0x000000010000d3f5 Python`PyObject_Call + 69, stop reason = EXC_BAD_ACCESS (code=1, address=0x18)
frame #0: 0x000000010000d3f5 Python`PyObject_Call + 69
Python`PyObject_Call + 69:
-> 0x10000d3f5: movl 24(%rax), %edx
0x10000d3f8: incl %edx
0x10000d3fa: movl %edx, 24(%rax)
0x10000d3fd: leaq 2069148(%rip), %rax ; _Py_CheckRecursionLimit
(lldb) bt
* thread #1: tid = 0x1c03, 0x000000010000d3f5 Python`PyObject_Call + 69, stop reason = EXC_BAD_ACCESS (code=1, address=0x18)
frame #0: 0x000000010000d3f5 Python`PyObject_Call + 69
frame #1: 0x00000001000d5197 Python`PyEval_CallObjectWithKeywords + 87
frame #2: 0x0000000201100d8e emb`instantiate + 30 at emb.c:9
为什么对instantiate()的调用仅从ctypes失败?该函数只在调用python lib时崩溃,所以也许某些解释器状态被ctypes FFI调用了?
答案 0 :(得分:2)
感谢Armin Rigo的暗示。问题是通过ctypes.CDLL()加载的库创建了在调用本机代码时释放GIL的函数。据我所知,这意味着为了让本机函数回调到python代码,它需要首先使用python C API获取GIL。
更容易的替代方法是使用ctypes.PyDLL(),它不会释放GIL(它还会检查python错误标志)。文档说,“因此,这仅对直接调用Python C API函数有用。”我的代码更间接,因为我有python代码调用我自己的C函数,然后调用python C API,但问题是相同的。