我正在开发一个使用 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 ()