Py_Finalize() 会导致 Python 3.9 出现分段错误,但不会导致 Python 2.7

2024-01-13

我正在开发一个使用 C++ matplotlib 包装器的项目matplotlibcpp.h https://github.com/lava/matplotlib-cpp.

使用这个原始头文件的最小示例是

    #include "matplotlibcpp.h"

    namespace plt = matplotlibcpp;

    int main() {
        plt::plot({1,3,2,4});
        plt::show();
    }

注意:似乎分段错误并不依赖于上面的示例,而是真正出现在任何调用该函数中的函数的程序中。mathplotlibcpp.h头文件。我选择这个绘图示例是因为实际绘图将起作用,您将看到绘图,但一旦关闭它并且程序完成,您将得到分段错误。此外,它是项目 github 页面上的官方示例之一。

您还可以将主函数中的两行替换为例如plt::figure()你仍然会得到一个工作程序和一个分段错误在执行的最后.

用python2.7编译它似乎工作正常

g++ minimal.cpp -std=c++11 -I/usr/include/python2.7 -I/home/<user>/.local/lib/python2.7/site-packages/numpy/core/include/ -lpython2.7

$ ldd a.out 
    linux-vdso.so.1 (0x00007ffe1f3f7000)
    libpython2.7.so.1.0 => /usr/lib/libpython2.7.so.1.0 (0x00007f8320f8f000)
    libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x00007f8320db2000)
    libm.so.6 => /usr/lib/libm.so.6 (0x00007f8320c6d000)
    libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x00007f8320c53000)
    libc.so.6 => /usr/lib/libc.so.6 (0x00007f8320a86000)
    libpthread.so.0 => /usr/lib/libpthread.so.0 (0x00007f8320a65000)
    libdl.so.2 => /usr/lib/libdl.so.2 (0x00007f8320a5c000)
    libutil.so.1 => /usr/lib/libutil.so.1 (0x00007f8320a57000)
    /lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007f83211c2000)

用python3.9编译它似乎会导致分段错误

g++ minimal.cpp -std=c++11 -I/usr/include/python3.9 -I/home/pascal/.local/lib/python3.9/site-packages/numpy/core/include/ -lpython3.9

here ./a.out结果是分段错误(核心转储)

$ ldd a.out 
    linux-vdso.so.1 (0x00007fff8dbc5000)
    libpython3.9.so.1.0 => /usr/lib/libpython3.9.so.1.0 (0x00007f60176ec000)
    libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x00007f601750f000)
    libm.so.6 => /usr/lib/libm.so.6 (0x00007f60173ca000)
    libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x00007f60173b0000)
    libc.so.6 => /usr/lib/libc.so.6 (0x00007f60171e3000)
    libpthread.so.0 => /usr/lib/libpthread.so.0 (0x00007f60171c2000)
    libdl.so.2 => /usr/lib/libdl.so.2 (0x00007f60171b9000)
    libutil.so.1 => /usr/lib/libutil.so.1 (0x00007f60171b4000)
    /lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007f6017adf000)

两者都是在使用 arch linux 和 g++ 版本 10.2.0 的系统上编译的。

这是一issue https://github.com/lava/matplotlib-cpp/issues/248在他们的 git 中找到了,但到目前为止,没有人想出解决方案。

现在我将问题隔离为调用Py_Finalize()。对于Python3,这调用Py_FinalizeEx()。所以Python2和Python3之间是有区别的。

现在在matplotlibcpp.h file Py_Finalize()在解构函数中调用:

~_interpreter() {
    Py_Finalize();
}

如果您将其注释掉,您就可以消除分段错误。现在我真的对这个终结函数感到困惑,因为文档状态(对于 python3)

错误和警告:模块和模块中对象的破坏是 以随机顺序完成;这可能会导致析构函数(del() 方法) 当它们依赖于其他对象(甚至函数)或模块时会失败。 Python 加载的动态加载的扩展模块不是 卸载。 Python解释器分配的少量内存 可能无法释放(如果您发现泄漏,请报告)。内存绑定 对象之间的循环引用没有被释放。一些记忆 由扩展模块分配的空间可能无法释放。一些扩展可能 如果调用它们的初始化例程超过 一次;如果应用程序调用 Py_Initialize() 并且 Py_FinalizeEx() 多次。

现在还有一个Kill()头文件中的函数显式调用解构函数,但从未使用过。

现在,似乎只有当我们超出范围时才会调用解构函数,即它们从不使用free() or delete。我认为它只是尝试释放已经释放的东西,但弄清楚它有点困难,因为我对 C Python API 非常不熟悉。

堆栈跟踪:(我希望我正确安装了 python 调试符号。不知道为什么 Qt5 小部件符号不显示。)

注意:我编译了下面的堆栈跟踪-std=c++17 -Wall -g

另请注意该函数matplotlibcpp::detail::_interpreter::interkeeper(bool)显式调用解构函数,请参阅kill()。我提到这一点是因为下面的堆栈跟踪中提到了这个函数 - 但我不确定为什么。该函数的源代码有以下注释:

/* 
    For now, _interpreter is implemented as a singleton since its currently not possible to have
   multiple independent embedded python interpreters without patching the python source code
   or starting a separate process for each. [1]
   Furthermore, many python objects expect that they are destructed in the same thread as they
   were constructed. [2] So for advanced usage, a `kill()` function is provided so that library
   users can manually ensure that the interpreter is constructed and destroyed within the
   same thread.
     1: http://bytes.com/topic/python/answers/793370-multiple-independent-python-interpreters-c-c-program
     2: https://github.com/lava/matplotlib-cpp/pull/202#issue-436220256
   */

堆栈跟踪:

Thread 1 "MAIN" received signal SIGSEGV, Segmentation fault.
0x00007fffde884225 in ?? () from /usr/lib/libQt5Widgets.so.5
(gdb) bt
#0  0x00007fffde884225 in ?? () from /usr/lib/libQt5Widgets.so.5
#1  0x00007fffdf14540a in ?? () from /usr/lib/python3.9/site-packages/PyQt5/QtWidgets.abi3.so
#2  0x00007fffe2bc67eb in ?? () from /usr/lib/python3.9/site-packages/PyQt5/QtCore.abi3.so
#3  0x00007ffff7d0ea5c in cfunction_vectorcall_NOARGS (func=0x7fffe2cccb80, args=<optimized out>, nargsf=<optimized out>, kwnames=<optimized out>) at Objects/methodobject.c:485
#4  0x00007ffff7e0ca69 in atexit_callfuncs (module=<optimized out>) at ./Modules/atexitmodule.c:93
#5  0x00007ffff7c744e7 in call_py_exitfuncs (tstate=0x555555597240) at Python/pylifecycle.c:2374
#6  0x00007ffff7dfc627 in Py_FinalizeEx () at Python/pylifecycle.c:1373
#7  0x000055555555926d in matplotlibcpp::detail::_interpreter::~_interpreter (this=0x55555555e620 <matplotlibcpp::detail::_interpreter::interkeeper(bool)::ctx>, 
    __in_chrg=<optimized out>) at /home/pascal/test/cpp/foo/matplotlibcpp.h:288
#8  0x00007ffff76d24a7 in __run_exit_handlers () from /usr/lib/libc.so.6
#9  0x00007ffff76d264e in exit () from /usr/lib/libc.so.6
#10 0x00007ffff76bab2c in __libc_start_main () from /usr/lib/libc.so.6
#11 0x000055555555646e in _start ()

我无法轻松访问 Linux 来测试它,但我想我现在明白发生了什么。

  1. matplotlibcpp使用静态变量来保存Python解释器(参见里面的第129行interkeeper(bool should_kill))。与 C++ 静态函数变量一样,它在第一次调用函数时初始化,并在程序退出时销毁(参考 https://en.cppreference.com/w/cpp/language/storage_duration#Static_local_variables).

  2. When main完成,libc为所有共享库和您的程序运行清理例程(即__run_exit_handlers在堆栈跟踪中)。由于您的程序是 C++ 程序,因此其退出处理程序的一部分会破坏所有使用的静态变量。其中之一是Python解释器。它的析构函数调用Py_Finalize()这是Python 的清理例程。到现在为止,一切都很好。

  3. Python也有类似的atexit允许来自任何地方的 Python 代码注册在解释器关闭期间应调用的函数的机制。显然,这里选择使用的后端 matplotlib 是 PyQt5。它似乎注册了这样的 atexit 回调。

  4. PyQt5 的回调被调用并崩溃。请注意,现在这是内部 PyQt5 代码。为什么会崩溃?我的“有根据的”猜测是 Qt 的library退出处理程序是已经打电话了在步骤 2 中,在调用程序的退出处理程序之前。这显然会导致库中出现一些奇怪的状态(也许某些对象被释放?)并崩溃。

这留下了两个有趣的问题:

  1. 如何解决这个问题?解决方案应该是破坏ctx在程序退出之前,因此 Python 解释器会在任何共享库自行终止之前被破坏。众所周知,静态寿命会导致类似的问题 https://en.cppreference.com/w/cpp/language/siof。如果更改 matplotlibcpp 的接口不使用全局静态不是一个可能的解决方案,我认为你真的必须手动调用plt::detail::_interpreter::kill()在主要功能的末尾。你应该能够使用atexit() https://en.cppreference.com/w/cpp/utility/program/atexit并注册一个回调,在库拆卸之前杀死解释器 - 但我还没有测试过它。

  2. 为什么这会起作用?我的猜测是,也许 PyQt5 回调中的某些内容发生了变化,导致了这次崩溃,或者您在 Python 2 中使用了不同的后端。如果在程序退出之前没有其他库破坏性地终止,那就没问题。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Py_Finalize() 会导致 Python 3.9 出现分段错误,但不会导致 Python 2.7 的相关文章

  • X 轴和 Z 轴上的 Quaternion.Slerp,无 Y 轴

    I am trying to rotate the Player about X Y and Z axis The Y axis should not move from last angle Example if I rotate 45
  • 使用 STL 流时如何格式化我自己的对象?

    我想将我自己的对象输出到 STL 流 但具有自定义格式 我想出了这样的东西 但由于我之前从未使用过 locale 和 imbue 所以我不知道这是否有意义以及如何实现 MyFacet 和operator 所以我的问题是 这是否有意义以及如何
  • 如何在 C++ 中正确使用 cin.fail()

    我正在编写一个程序 从用户那里获取整数输入cin gt gt iUserSel 如果用户输入一个字母 程序就会进入无限循环 我试图用下面的代码来阻止这种情况 但程序进入无限循环并打印出 错误 输入 我该如何修复我的程序 cin gt gt
  • 当我尝试传递临时地址作为参数时,它是一个 UB 吗?

    对于以下 C 代码 include
  • 在 matplotlib 中将 3D 背景更改为黑色

    我在将 3D 图表的背景更改为黑色时遇到问题 这是我当前的代码 当我将facecolor设置为黑色时 它会将图表内部更改为灰色 这不是我想要的 fig plt figure fig set size inches 10 10 ax plt
  • Django:显示管理员验证错误的自定义错误消息

    我正在使用 Django 1 2 4 我有一个模型 其中有一个需要验证的字段 当验证失败时 我想向用户显示自定义错误消息 模型编辑是在管理界面中完成的 这就是我目前正在做的事情 def clean fields self exclude N
  • 列表中的“u”是什么意思?

    这是我第一次遇到这种情况 刚刚打印了一个列表 每个元素似乎都有一个u在它前面 即 u hello u hi u hey 它是什么意思 为什么列表的每个元素前面都会有这个 由于我不知道这种情况有多常见 如果您想了解我是如何遇到它的 我会很乐意
  • Django INSTALLED_APPS 的命名约定是如何工作的?

    该网站上的教程创建了一个名为 polls 的应用程序 它使用 django 1 9 所以在 INSTALLED APPS 中它是 polls apps PollsConfig 我正在观看一个教程 他将应用程序命名为新闻通讯 并且在 INST
  • 从 python 文件调用 Julia 函数

    我能够创建一个 docker 环境 然后按照这个线程我有一个用 Julia 编写的高性能函数 如何从 Python 中使用它 https stackoverflow com questions 64241264 i have a high
  • 为什么这个位图图像在加载后会改变大小?

    快速提问 我有这个1000 1000位图图像 我使用这个例程来加载它 private BitmapSource initialBitmap new BitmapImage new Uri C Users Desktop Original b
  • 在 clang 中向量化函数

    我正在尝试根据此用 clang 对以下函数进行矢量化铿锵参考 http llvm org docs Vectorizers html 它采用字节数组向量并根据以下条件应用掩码this RFC https www rfc editor org
  • 具有多个父项的 Qt 树模型

    我想构建一棵树 其中一个元素可以引用另一个元素 我想要构建的树是 像这样的东西 A B C D E F P this is a pointer to C D first child of C E second child of C I fo
  • 给定一个字符串,如何删除所有重复的连续字母?

    如何从字符串中删除两个连续的字母 例如 a str hii thherre 应该成为 hi there 我尝试这样做 a str join sorted set a str key a str index 但是 我得到 hi ter 是的
  • Windows Phone 的 JSON 反序列化

    我正在尝试反序列化以下 JSON 但我真的不知道如何使用 JSON net 来完成这项工作 我正在使用 C 和 JSON Net 库 我的 JSON 如下 found 3 bounds 43 54919 172 62148 43 54487
  • 查找数组中的多个索引

    假设我有一个像这样的数组 string fruits watermelon apple apple kiwi pear banana 是否有一个内置函数可以让我查询 apple 的所有索引 例如 fruits FindAllIndex ap
  • C# 多维数组解析

    我有一个多维数组 内容在调试器中看起来像这样 数组设置为 String s new String 6 4 A B Yes C A B Yes C A B No C A B Yes C A B Yes C A B Yes C A B No C
  • Django 按小时过滤

    我找到了那个链接 http code djangoproject com attachment ticket 8424 time filters diff http code djangoproject com attachment tic
  • 为什么存在系统调用

    我一直在阅读有关系统调用及其在 Linux 中如何工作的内容 我还有更多的阅读要做 但我读过的一件事都没有回答 那就是 为什么我们需要系统调用 我知道系统调用是用户空间程序要求内核执行某些操作的请求 但我的问题基本上是 为什么用户空间程序本
  • 尝试后终于没有被调用

    由于某种原因 在我的控制台应用程序中 我无法运行我的finally 块 我编写这段代码是为了测试finally块是如何工作的 所以它非常简单 static void Main int i 0 try int j 1 i Generate a
  • 如何将模型绑定到动态创建的类 nancyfx

    首先感谢任何愿意查看我的问题的人 我对 Nancyfx 还很陌生 在尝试将 JSON 有效负载绑定到动态创建的类时遇到问题 我按照这篇文章中的代码动态创建了该类 在C 中动态创建一个类 https stackoverflow com que

随机推荐

  • 在 JSF 中动态创建响应

    我想在我的 JSF 页面上有一个链接 单击此链接时 Java Web Start 应用程序将启动 该应用程序根本不影响当前页面 该应用程序会自行启动 问题是 我需要动态生成启动 Java Web Start 应用程序的 JNLP 文件 基本
  • 如何在 Pandas 数据框中堆叠行以获得一个“长行”?

    假设我有一个 4 行 3 列的数据框 我想水平堆叠行 这样我就得到一行 12 列 如何操作以及如何处理冲突的列名称 您可以通过以下方式实现这一目标stack计算框架以产生一系列所有值 然后我们希望使用以下方法将其转换回 dfto frame
  • 将二进制浮点“1101.11”转换为十进制(13.75)的正确算法?

    我用C编写了一个程序来转换浮点数以二进制表示 http kipirvine com asm workbook floating tut htm 1101 11 化为小数 13 75 但是 我似乎无法从算法中获得正确的值 将二进制浮点数转换为
  • JavaScript 中的“闭包”到底指的是什么?

    我理解闭包是什么 但我在理解这个术语到底是什么时遇到了一些困难closure指 我在许多网站上看到过这个术语 但很少有人就其实际定义达成一致 是保存在栈帧上的变量吗 是返回的函数吗 它是外部函数的范围吗 它是内部 返回 函数的范围吗 是不是
  • 如何在 Swift 3 中测试 URL 并获取状态代码?

    我正在使用最新版本的 Xcode 撰写本文时为 8 1 它使用 Swift 3 0 我想做的就是获取一个字符串 将其转换为 URL 并测试该 URL 看看它是否会给出 404 错误 我已经能够使用以下方法创建 URL 和 URLReques
  • Node.JS 内核模式线程

    我试图弄清楚 Node JS 其 Windows 版本 是如何在幕后工作的 我知道有用户模式和内核模式线程 并且我知道处理模型如下所示 我还知道从内核模式线程移动到用户模式线程被认为是上下文切换 Node JS C 非阻塞工作线程是内核模式
  • Angular2 在模板和变更检测中的功能

    我试图在服务中构建一种方法 检查是否应根据当前用户的权限向当前用户显示导航按钮 我知道这只是装饰性的 安全性 因此这是放置在模板内的按钮
  • 尾随返回类型中的占位符是否会覆盖初始占位符?

    g 似乎接受任意组合auto and decltype auto 作为初始和尾随返回类型 int a auto f return a int auto g gt auto return a int auto h gt decltype au
  • 关于 DSO 引用隐藏符号的警告究竟意味着什么?

    我在将某些共享库与 g 链接时遇到问题 它给了我这样的警告 hidden symbol XXX in YYY is referenced by DSO usr lib 我已经阅读了一些有关特定问题的相关问题 但我想从整体上理解它 这个警告是
  • 如何授予jnlp文件中依赖lib的所有权限?

    我在使用 Java Web Start 时遇到了一个严重的问题 我无法理解 情况如下 我有一个 JavaFX 应用程序 编写为 Java8 我想使用 Java WebStart 进行部署 直到最近 我还使用 HTTP 与服务器进行通信 一切
  • 仅在 Spring Data Rest 中为子资源级别启用存储库?

    我有 2 个 jpa 实体 Document 和 DispatchDetail 它们具有一对多关系 即一个文档可以有一个dispatchDetails 列表 我为每个实体创建了 2 个存储库 现在我要尝试文档 GET http localh
  • Django MakeMessages 在 Windows 中缺少 xgettext

    在 Windows 7 上运行 Django 我目前正在尝试使用 django 书籍第 19 章中的说明翻译几个 Django 模板 我已向模板添加了翻译标签 加载了 I18N 并修改了 django 设置 然后我运行 django adm
  • 快速地,为什么当协议有初始化器时我不能实例化它?

    据我所知 通常我无法实例化协议 但是 如果我在协议中包含一个初始化程序 那么编译器肯定知道当结构或类稍后使用该协议时 它将有一个可以使用的 init 吗 我的代码如下所示 protocol Solution var answer Strin
  • 无法连接到服务器:net/http:TLS 握手超时

    在 minikube for windows 上 我在 kubernetes 集群上创建了一个部署 然后尝试通过将副本从 1 更改为 2 来扩展它 之后 kubectl 挂起 我的磁盘使用率为 100 我的部署中只有一个容器 apiVers
  • 单击按钮后立即执行 SwiftUI 按钮操作,而不是单击释放时

    我想在 SwiftUI Button 中单击 点击按钮后立即调用该操作 我怎样才能实现这个 这是一种可能的方法 使用自定义ButtonStyle注入自定义触地动作 使用 Xcode 12 iOS 14 进行测试 struct Pressed
  • com.sun.ws.rs.ext.RuntimeDelegateImpl 错误

    请帮忙 目前我正在构建一个系统 允许一些Web服务 Axis2 调用restful jersey 1 12 场景是这样的 客户端 gt webservice Axis2 gt Restful services Jersey 1 12 在 t
  • 如何使用 SqlCommand 创建带有参数化数据库名称的数据库?

    简而言之 我有两个简单的助手 private SqlCommand CreateCommand string text SqlCommand cmd new SqlCommand cmd Connection connection cmd
  • Tinymce 4.x 扩展插件

    我正在寻找一些有关如何扩展现有的tinymce 4 x 插件的示例 例如 链接 插件 链接插件打开一个对话框窗口 我想做的是在对话框打开时添加一个事件并修改正文 插入一些带有单击事件的额外 HTML 做得好似乎是有问题的 我想避免一些 顶部
  • 生成两个相关的随机向量

    我想生成两个具有指定相关性的随机向量 第二个向量的每个元素必须与第一个向量的相应元素相关并且相互独立 我怎样才能在 MATLAB 中做到这一点 顺便说一句 第一个向量的元素不具有相同的分布 我的意思是第一个向量的每个元素应该具有不同的方差
  • Py_Finalize() 会导致 Python 3.9 出现分段错误,但不会导致 Python 2.7

    我正在开发一个使用 C matplotlib 包装器的项目matplotlibcpp h https github com lava matplotlib cpp 使用这个原始头文件的最小示例是 include matplotlibcpp