0

Python C API で非常に奇妙な問題が発生しています。PyObject 'device' メンバーを含むグローバル スコープの構造体 'dcon' があります。

static status_t get_mac_addr(uint8_t const ** addr, 
                             size_t * const size){
    static uint8_t device_mac_addr[6] = {0};
    *addr = device_mac_addr;
    *size = sizeof device_mac_addr;

    if(PyObject_HasAttrString(dcon.device, "mac_address") == 1){
       ...        
    }
    return 0;
}

「mac_address」属性が存在する場合はいつでも、コードは正常に実行されるようです。奇妙なことに、「mac_address」が定義済みの属性ではない場合、segault ( Segmentation fault (core dumped)) が発生します。segfault が発生したときのバックトレースは次のとおりです。

(gdb) bt
#0  0x00000000004ca607 in PyErr_Restore ()
#1  0x00000000004aa29f in PyErr_Format ()
#2  0x000000000049e6ee in _PyObject_GenericGetAttrWithDict ()
#3  0x0000000000486387 in PyObject_GetAttrString ()
#4  0x00000000004ea7d7 in PyObject_HasAttrString ()
#5  0x00007ffff4f2056d in get_mac_addr (size=0x7ffff4f1cd28, addr=<optimized out>) at config.c:165

私は Python C API の初心者です。私の最初の考えは、参照カウントで何か間違ったことをしているということですが、頭を包むことができないようです。

4

1 に答える 1

1

マルチスレッド アプリケーションでは、Python C API 関数を呼び出すすべてのスレッドは、最初にそのスレッドがグローバル インタープリター ロックを保持していることを確認する必要があります。Python コードから呼び出される関数の場合、ロックは既に保持されているため、これは問題ではありません。

これは、インタープリターの外部から呼び出されるコード (コールバックなど) には当てはまりません。

このような場合、Python API 関数を安全に呼び出す前に GIL を取得する必要があります。

PyGILState_STATE gstate;
gstate = PyGILState_Ensure();

...

PyGILState_Release(gstate);

これにより、GIL が確実に保持されるだけでなく、Python の外部で作成されたスレッドの Python スレッド状態も作成されます (直接 OS 呼び出しを使用し、 ではありませんthread.start_new_thread())。

Python API の呼び出し中に発生する可能性のある Python 例外は、GIL を解放する前に処理する必要があります。

別のスレッドが現在ロックを保持している場合 (たとえば、Python コードを実行しているなど)、Ensure 呼び出しは、別のスレッドによってロックが解放されるまでブロックされることに注意してください。

ロックが既に取得されている場合でも、Ensure() へのすべての呼び出しに対応する Release() への呼び出しがある場合は、これらの呼び出しを使用してもかまいません。

于 2012-10-28T08:42:22.240 に答える