2012-12-04 27 views
2

當我通過ctypes調用的c函數實例化python對象時,嵌入式Python 3.3程序出現segfaults。在通過ctypes調用的c函數中實例化python對象

設置解釋器後,我可以成功地從C主要實例化一個蟒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的,裏面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 

爲什麼調用實例()從失敗只有ctypes?這個函數在調用python庫的時候只會崩潰,所以或許某些解釋器狀態正在被ctypes FFI調用消滅?

+0

dy_test()如何獲得實例化?你的日誌消息,如果它通過實例化,表明PyObject_CallObject已完成。否則,你不會看到LOGPY輸出。你可以在Python中嘗試PyLongObject或其他標準的可調用嗎?這會告訴你,如果這是你的類型設置的問題。 –

+0

對不起,我把dy_test改名爲清晰的問題;固定。傳遞&PyLong_Type以同樣的方式失敗;我會相應地減少repro。 – gwk

+2

如果用'PyDLL'替換'CDLL',它會更好嗎? –

回答

2

感謝Armin Rigo的提示。問題是通過ctypes.CDLL()加載的庫創建的函數在調用本機代碼時釋放GIL。據我所知,這意味着爲了讓本地函數回調python代碼,它需要首先使用python C API獲取GIL。

更簡單的選擇是使用ctypes.PyDLL(),它不釋放GIL(它也檢查python錯誤標誌)。該文檔說:「因此,這只是直接調用Python C API函數纔有用。」我的代碼更加間接,因爲我將python代碼調用到我自己的C函數中,然後調用python C API,但問題是相同的。