use*_*385 9 python embed numpy segmentation-fault
我面临的问题类似于Py_initialize/Py_Finalize没有使用numpy工作两次 .. C中的基本编码:
Py_Initialize();
import_array();
//Call a python function which imports numpy as a module
//Py_Finalize()
Run Code Online (Sandbox Code Playgroud)
该程序处于循环中,如果python代码将numpy作为导入模块之一,则会产生seg错误.如果我删除numpy,它工作正常.
作为临时工作我试图不使用Py_Finalize(),但这导致巨大的内存泄漏[观察到TOP的内存使用量不断增加].我试过但不明白我发布的链接中的建议.有人可以建议最好的方法来完成通话,同时有像numpy这样的导入.
谢谢santhosh.
我最近遇到了一个非常相似的问题,并开发了一种适合我的目的的解决方法,所以我想我会在这里写它,希望它可以帮助其他人。
我使用一些后处理管道,我可以为其编写一个自己的函子来处理通过管道传递的一些数据,并且我希望能够使用 Python 脚本进行某些操作。
问题是我唯一可以控制的是函子本身,它有时会在我无法控制的情况下被实例化和销毁。我还有一个问题,即使我不调用Py_Finalize管道,一旦我通过管道传递另一个数据集,有时也会崩溃。
对于那些不想阅读整个故事并直截了当的人,这是我的解决方案的要点:
我的解决方法背后的主要思想不是链接 Python 库,而是使用动态加载它dlopen,然后使用dlsym. 完成后,您可以调用Py_Initialize()后跟任何您想对 Python 函数执行的操作,然后Py_Finalize()在完成后调用。然后,可以简单地卸载 Python 库。下次您需要使用 Python 函数时,只需重复上述步骤,Bob 就是您的叔叔。
但是,如果您在Py_Initialize和之间的任何一点导入 NumPy Py_Finalize,您还需要在程序中查找所有当前加载的库并使用dlclose.
我上面提到的主要思想不是链接到 Python 库。相反,我们要做的是使用 dlopen() 动态加载 Python 库:
#include ... void* pHandle = dlopen("/path/to/library/libpython2.7.so", RTLD_NOW | RTLD_GLOBAL);
上面的代码加载 Python 共享库并返回它的句柄(返回类型是一个模糊的指针类型,因此是void*)。第二个参数 ( RTLD_NOW | RTLD_GLOBAL) 用于确保符号正确导入当前应用程序的范围。
一旦我们有了指向加载库句柄的指针,我们就可以使用该dlsym函数在该库中搜索它导出的函数:
#include <dlfcn.h>
...
// Typedef named 'void_func_t' which holds a pointer to a function with
// no arguments with no return type
typedef void (*void_func_t)(void);
void_func_t MyPy_Initialize = dlsym(pHandle, "Py_Initialize");
Run Code Online (Sandbox Code Playgroud)
该dlsym函数有两个参数:一个指向我们之前获得的库句柄的指针和我们要查找的函数的名称(在本例中为Py_Initialize)。一旦我们获得了我们想要的函数的地址,我们就可以创建一个函数指针并将其初始化为该地址。要实际调用该Py_Initialize函数,只需编写:
MyPy_Initialize();
Run Code Online (Sandbox Code Playgroud)
对于 Python C-API 提供的所有其他函数,只需添加dlsym对其返回值的调用和初始化函数指针,然后使用这些函数指针代替 Python 函数。只需知道 Python 函数的参数和返回值,即可创建正确类型的函数指针。
一旦我们完成了 Python 函数并Py_Finalize使用类似于一对一的过程调用,Py_Initialize可以通过以下方式卸载 Python 动态库:
dlclose(pHandle);
pHandle = NULL;
Run Code Online (Sandbox Code Playgroud)
不幸的是,这并不能解决导入 NumPy 时出现的分段错误问题。问题来自这样一个事实,即 NumPy 还使用dlopen(或等效的东西)加载了一些库,而当您调用Py_Finalize. 实际上,如果您列出程序中所有已加载的库,您会注意到在使用 关闭 Python 环境后Py_Finalize,然后调用dlclose,某些 NumPy 库将保持加载在内存中。
解决方案的第二部分需要列出调用后保留在内存中的所有 Python 库dlclose(pHandle);。然后,对于这些库中的每一个,抓住它们的句柄,然后调用dlclose它们。之后,它们应该被操作系统自动卸载。
幸运的是,Windows 和 Linux 下都有功能(抱歉,MacOS,找不到适合您的情况...): - Linux:dl_iterate_phdr
- Windows:EnumProcessModules结合OpenProcess和GetModuleFileNameEx
一旦您阅读了有关dl_iterate_phdr以下内容的文档,这将变得相当简单:
#include <link.h>
#include <string>
#include <vector>
// global variables are evil!!! but this is just for demonstration purposes...
std::vector<std::string> loaded_libraries;
// callback function that gets called for every loaded libraries that
// dl_iterate_phdr finds
int dl_list_callback(struct dl_phdr_info *info, size_t, void *)
{
loaded_libraries.push_back(info->dlpi_name);
return 0;
}
int main()
{
...
loaded_libraries.clear();
dl_iterate_phdr(dl_list_callback, NULL);
// loaded_libraries now contains a list of all dynamic libraries loaded
// in your program
....
}
Run Code Online (Sandbox Code Playgroud)
基本上,该函数dl_iterate_phdr循环遍历所有加载的库(以它们加载的相反顺序),直到回调返回除0列表之外的其他内容或到达列表末尾。为了保存列表,回调简单地将每个元素添加到一个std::vector全局变量中(例如,显然应该避免全局变量并使用一个类)。
在 Windows 下,事情变得有点复杂,但仍然可以管理:
#include <windows.h>
#include <psapi.h>
std::vector<std::string> list_loaded_libraries()
{
std::vector<std::string> m_asDllList;
HANDLE hProcess(OpenProcess(PROCESS_QUERY_INFORMATION
| PROCESS_VM_READ,
FALSE, GetCurrentProcessId()));
if (hProcess) {
HMODULE hMods[1024];
DWORD cbNeeded;
if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) {
const DWORD SIZE(cbNeeded / sizeof(HMODULE));
for (DWORD i(0); i < SIZE; ++i) {
TCHAR szModName[MAX_PATH];
// Get the full path to the module file.
if (GetModuleFileNameEx(hProcess,
hMods[i],
szModName,
sizeof(szModName) / sizeof(TCHAR))) {
#ifdef UNICODE
std::wstring wStr(szModName);
std::string tModuleName(wStr.begin(), wStr.end());
#else
std::string tModuleName(szModName);
#endif /* UNICODE */
if (tModuleName.substr(tModuleName.size()-3) == "dll") {
m_asDllList.push_back(tModuleName);
}
}
}
}
CloseHandle(hProcess);
}
return m_asDllList;
}
Run Code Online (Sandbox Code Playgroud)
这种情况下的代码比 Linux 情况下的代码稍长,但主要思想是相同的:列出所有加载的库并将它们保存到std::vector. 不要忘记还将您的程序链接到Psapi.lib!
现在我们可以列出所有加载的库,你需要做的就是在那些来自加载 NumPy 的库中找到,抓住它们的句柄,然后调用dlclose该句柄。如果您使用 dlfcn-win32 库,以下代码将适用于 Windows 和 Linux。
#ifdef WIN32
# include <windows.h>
# include <psapi.h>
# include "dlfcn_win32.h"
#else
# include <dlfcn.h>
# include <link.h> // for dl_iterate_phdr
#endif /* WIN32 */
#include <string>
#include <vector>
// Function that list all loaded libraries (not implemented here)
std::vector<std::string> list_loaded_libraries();
int main()
{
// do some preprocessing stuff...
// store the list of loaded libraries now
// any libraries that get added to the list from now on must be Python
// libraries
std::vector<std::string> loaded_libraries(list_loaded_libraries());
std::size_t start_idx(loaded_libraries.size());
void* pHandle = dlopen("/path/to/library/libpython2.7.so", RTLD_NOW | RTLD_GLOBAL);
// Not implemented here: get the addresses of the Python function you need
MyPy_Initialize(); // Needs to be defined somewhere above!
MyPyRun_SimpleString("import numpy"); // Needs to be defined somewhere above!
// ...
MyPyFinalize(); // Needs to be defined somewhere above!
// Now list the loaded libraries again and start manually unloading them
// starting from the end
loaded_libraries = list_loaded_libraries();
// NB: this below assumes that start_idx != 0, which should always hold true
for(std::size_t i(loaded_libraries.size()-1) ; i >= start_idx ; --i) {
void* pHandle = dlopen(loaded_libraries[i].c_str(),
#ifdef WIN32
RTLD_NOW // no support for RTLD_NOLOAD
#else
RTLD_NOW|RTLD_NOLOAD
#endif /* WIN32 */
);
if (pHandle) {
const unsigned int Nmax(50); // Avoid getting stuck in an infinite loop
for (unsigned int j(0) ; j < Nmax && !dlclose(pHandle) ; ++j);
}
}
}
Run Code Online (Sandbox Code Playgroud)
此处显示的示例捕获了我的解决方案背后的基本思想,但当然可以改进以避免全局变量并促进易用性(例如,我编写了一个单例类,该类在加载 Python 库后处理所有函数指针的自动初始化)。
我希望这对将来的某人有用。
dl_iterate_phdr: https://linux.die.net/man/3/dl_iterate_phdrOpenProcess: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684320(v=vs.85).aspxEnumProcess: https://msdn.microsoft.com/en-us/library/windows/desktop/ms682629(v=vs.85).aspxGetModuleFileNameEx: https://msdn.microsoft.com/en-us/library/windows/desktop/ms683198(v=vs.85).aspx我不太确定您似乎不理解Py_initialize / Py_Finalize not work times with numpy中发布的解决方案。发布的解决方案非常简单:每次程序执行时仅调用 Py_Initialize 和 Py_Finalize 一次。不要每次运行循环时都调用它们。
我假设您的程序在启动时运行一些初始化命令(仅运行一次)。在那里调用 Py_Initialize。永远不要再打电话了。另外,我假设当你的程序终止时,它有一些代码来拆除东西,转储日志文件等。在那里调用 Py_Finalize 。Py_Initialize 和 Py_Finalize 并不是为了帮助您管理 Python 解释器中的内存。不要为此使用它们,因为它们会导致您的程序崩溃。相反,使用 Python 自己的函数来删除您不想保留的对象。
如果您确实必须在每次运行代码时创建一个新环境,则可以使用 Py_NewInterpreter 并创建一个子解释器,并使用 Py_EndInterpreter 稍后销毁该子解释器。它们记录在Python C API页面底部附近。这与拥有一个新的解释器类似,只是每次子解释器启动时模块不会重新初始化。