【问题标题】:Segfault when trying to call a Python function from C尝试从 C 调用 Python 函数时出现段错误
【发布时间】:2011-07-02 17:06:31
【问题描述】:

所以,我想从 C 中调用 Python 回调函数。

在某个时候,函数被发送到 C 并打包成这样的元组

PyObject *userData = Py_BuildValue("Oi",py_callback,some_number);

在那个区域的某个地方,我也是Py_INCREF(py_callback)
在程序的稍后时间,我想调用该函数

PyObject *py_callback;
int some_number;
PyArg_ParseTuple((PyObject*)userData,"Oi",&py_callback,&some_number); // returns true
PyObject *py_result = PyObject_CallFunctionObjArgs(py_callback,
                                                   /* ... */
                                                   NULL);

最后一次调用会引发分段错误。你有什么想法,为什么它会做这样的事情?

【问题讨论】:

  • 在调试模式下编译,在 GDB 中加载,并获得回溯。
  • 在调用 Python 之前,您是否确保持有 GIL?
  • 我发现了一个更可能是罪魁祸首的不同问题(请参阅我的答案)。如果这仍然不起作用,docs.python.org/dev/c-api/… 应该为您提供足够的 Google 素材,以寻找一些更容易理解的关于如何处理 CPython 解释器锁的解释。
  • @ncoghlan:你的建议是正确的:这个问题可以通过 GIL 状态来解决。如果您将您的评论表述为一个问题,我将接受它。

标签: python c function callback call


【解决方案1】:

当从 Python C API 获得奇怪的行为时,始终值得仔细检查您是否正确管理全局解释器锁(又名“GIL”)的状态:http://docs.python.org/c-api/init.html#thread-state-and-the-global-interpreter-lock

【讨论】:

  • 哦,对不起。那是一个错字。我相应地更正了这个问题。
  • 这仍然没有意义 - 你将什么作为第二个参数传递给 PyObject_CallFunctionArgs&some_number 将无法正常工作(这是错误的类型),并且您在当前版本中根本没有初始化 py_some_number。既然你想回调 Python,使用"OO" 并将数字保持为PyObject 是你想要做的。
  • 你是对的,当然。我实际上发现无论如何我都不需要那个号码。
  • 我用对 GIL 管理文档的引用替换了我的原始答案,因为该评论最终解决了真正的问题,而元组解析问题只影响了 SO 问题,而不影响真正的代码。
【解决方案2】:

我不确定为什么每个人都在这里谈论 GIL。你在任何地方发布它吗?我注意到一些可能的问题点,我将在下面列出。它与建议混合在一起。

我注意到您没有增加 userData 的引用计数。为什么不?你在存储它不是吗?为什么要将它存储在元组中?为什么不只保留两个变量(用于回调和数据),然后增加它们的引用计数,以便您拥有一个引用?

首先,检查所有函数的返回值是否为NULL。这将让您确保您实际上运行正常。使用PyObject_Print 在代码中的所有位置打印您感兴趣的对象以确保事情按预期进行可能也是一个好主意。

根据您对段错误发生位置的评论,我的猜测是,当您使用PyObject_CallFunctionObjArgs 调用回调函数时,您将错误数量的参数传递给回调函数。你能仔细检查一下吗?也许向我们展示您的回调函数定义,然后再次检查调用。

我看到的另一个可能的问题是,从命名上看,py_some_number 听起来像是您期望 Python 整数对象。不是这种情况。在PyArg_ParseTuple 之后,它将包含一个整数。

【讨论】:

  • C-callback 函数必须符合 Portaudio 的函数定义,所以userData 只能是一个指针。我检查并仔细检查了所有调用和参数,它们绝对正确。不过,感谢您提供有关增加引用计数的提示!
  • 那我很好奇。什么改变解决了这个问题?
  • 在调用 Python 之前插入 PyGILState_STATE gstate = PyGILState_Ensure();。后端实际上是在创建一个新线程,所以我不能在不事先确保线程状态的情况下调用 Python 函数。我不确定这是否有意义。我仍在为 SO 上的另一个问题中的线程问题苦苦挣扎。
  • 这太奇怪了。我一直在这样做(一段时间后调用传递给 C 的 python 函数)有一段时间没有遇到任何此类问题。我没有使用其他线程,但仍然。
  • 在搜索我的问题时,我在 Google 图书上遇到了“专家 Python 编程:通过学习编码成为 Python 大师”一书的第 297 页。它确实提到 GIL 限制是在 Python 中编码 C 扩展的著名限制。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-04
  • 1970-01-01
  • 1970-01-01
  • 2022-11-30
  • 2011-01-19
  • 2015-04-20
  • 1970-01-01
相关资源
最近更新 更多