3

ctypesによって呼び出されたac関数からPythonオブジェクトをインスタンス化すると、埋め込まれたPython3.3プログラムがsegfaultsします。

インタプリタを設定した後、cmainからpythonInt(およびカスタム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内でsegfaultを実行します。

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

instanceiate()の呼び出しがctypesからのみ失敗するのはなぜですか?関数はpythonlibを呼び出したときにのみクラッシュするので、おそらくいくつかのインタープリター状態がctypes FFI呼び出しによって変更されていますか?

4

1 に答える 1

3

ヒントをくれたArminRigoに感謝します。問題は、ctypes.CDLL()を介してロードされたライブラリが、ネイティブコードを呼び出すときにGILを解放する関数を作成することです。私が知る限り、これは、ネイティブ関数がPythonコードをコールバックするために、最初にPythonCAPIを使用してGILを取得する必要があることを意味します。

より簡単な代替方法は、GILを解放しないctypes.PyDLL()を使用することです(Pythonエラーフラグもチェックします)。ドキュメントには、「したがって、これはPythonCAPI関数を直接呼び出す場合にのみ役立ちます」と記載されています。私のコードはより間接的で、Pythonコードが自分のC関数を呼び出し、次にPython C APIを呼び出しますが、問題は同じです。

于 2012-12-28T16:40:01.427 に答える