从 python 调用在 cpp 中执行 python 的函数会出现 free() 无效指针错误

问题描述 投票:0回答:1

我编写了一个在 c++ 中使用 python c api 执行 python 字符串的函数 当我运行它执行 python 字符串时,该函数完美运行

当我制作 .so 文件,然后使用 cpp 从 python 端调用相同的函数时,就会出现问题

它在

free() invalid pointer
这条线上给出了磨损错误
Py_Initialize();
当我注释掉该行时,就会出现分段错误


我的runner.cpp代码是

#include <vector>
#include <dlfcn.h>
#include <iostream>
#include <sstream>
#include <cstdlib>
#include <cstring>

typedef void (*Py_Initialize_t)();
typedef int (*PyRun_SimpleString_t)(const char *);
typedef void (*Py_Finalize_t)();


std::string execute_command(const char* command) {
    FILE* pipe = popen(command, "r");
    if (!pipe) {
        return "Error executing command";
    }

    
    std::ostringstream output;
    char buffer[128];
    while (!feof(pipe)) {
        if (fgets(buffer, 128, pipe) != nullptr) {
            output << buffer;
        }
    }

    // Close the pipe and return the output
    pclose(pipe);
    return output.str();
}
std::vector<std::string> split_string(const std::string& input, char delimiter) {
    std::vector<std::string> tokens;
    std::istringstream iss(input);
    std::string token;
    while (std::getline(iss, token, delimiter)) {
        tokens.push_back(token);
    }
    return tokens;
}

void execute_Prog730(std::string program) {
    std::cout<<"Running Engine"<<std::endl;
    std::string python_version_output = execute_command("python3 -c \"import sys; print(sys.version)\"");
    size_t start_pos = python_version_output.find_first_of("0123456789");
    size_t end_pos = python_version_output.find_first_not_of("0123456789.", start_pos);
    std::string python_version = python_version_output.substr(start_pos, end_pos - start_pos);
    std::vector<std::string> parts = split_string(python_version, '.');

    std::ostringstream lib_name_stream;
    lib_name_stream << "libpython" << parts[0] << '.' << parts[1] << ".so";
    std::string lib_name = lib_name_stream.str();
    std::cout<<lib_name<<std::endl;
    void* handle = dlopen(lib_name.c_str(), RTLD_LAZY);
    if(!handle && lib_name.back()=='o'){
        std::string  lib_name_so1 = lib_name+".1";
        handle = dlopen(lib_name_so1.c_str(),RTLD_LAZY);
    }

    if (!handle) {
        std::cerr << "Failed to load Python library" << std::endl;
        //return 1;
    }

    auto Py_Initialize = (Py_Initialize_t)dlsym(handle, "Py_Initialize");
    auto PyRun_SimpleString = (PyRun_SimpleString_t)dlsym(handle, "PyRun_SimpleString");
    auto Py_Finalize = (Py_Finalize_t)dlsym(handle, "Py_Finalize");

    if (!Py_Initialize || !PyRun_SimpleString || !Py_Finalize) {
        std::cerr << "Failed to resolve Python functions" << std::endl;
        dlclose(handle);
    }
    std::cout<<"Debug"<<std::endl;
    Py_Initialize();
    std::cout<<"Debug"<<std::endl;
    PyRun_SimpleString(program.c_str());
    Py_Finalize();
    dlclose(handle);
}

我的 runner.h 是

#include <iostream>
#include "runner.cpp"

void execute_Prog730(std::string program) ;

我的main.cpp是

#include <iostream>
#include <cstring>
#include <string.h>

#include <runner.h>

extern "C" void run(const char* program){
    std::string pr(program);
    std::cout<<pr<<std::endl;
    execute_Prog730(pr);
}

我用来编译的命令

g++ -o main.so --shared -fPIC main.cpp -I/home/lakshit/Desktop/temp/include 

最后我的 main.py 是

import ctypes 

module = ctypes.CDLL('/home/lakshit/Desktop/temp/main.so')

func = module.run
func.argtypes = [ctypes.c_char_p]

func(b"print('hello')")

注意 我使用的是 ubuntu 22.04 某些版本需要添加 -ldl 命令才能成功编译 .so 文件

python c++ python-c-api
1个回答
1
投票

总结评论讨论

  1. 因为 python 已经加载,所以你不应该调用
    Py_Initialize
    Py_Finalize
  2. 由于 python dll 已经加载,您需要使用
    RTLD_NOLOAD
  3. 加载它
  4. 在CPP端调用python之前需要获取GIL。

以下代码解决了我系统上的问题。

#include <vector>
#include <dlfcn.h>
#include <iostream>
#include <sstream>
#include <cstdlib>
#include <cstring>

namespace {

enum PyGILState_STATE
{
    locked, unlocked
};

using PyRun_SimpleString_t = int (*)(const char *);
using PyGILState_Ensure_t =  PyGILState_STATE (*)(void);
using PyGILState_Release_t = void (*)(PyGILState_STATE);

}
std::string execute_command(const char* command) {
    FILE* pipe = popen(command, "r");
    if (!pipe) {
        return "Error executing command";
    }


    std::ostringstream output;
    char buffer[128];
    while (!feof(pipe)) {
        if (fgets(buffer, 128, pipe) != nullptr) {
            output << buffer;
        }
    }

    // Close the pipe and return the output
    pclose(pipe);
    return output.str();
}
std::vector<std::string> split_string(const std::string& input, char delimiter) {
    std::vector<std::string> tokens;
    std::istringstream iss(input);
    std::string token;
    while (std::getline(iss, token, delimiter)) {
        tokens.push_back(token);
    }
    return tokens;
}

void execute_Prog730(std::string program) {
    std::cout<<"Running Engine"<<std::endl;
    std::string python_version_output = execute_command("python3 -c \"import sys; print(sys.version)\"");
    size_t start_pos = python_version_output.find_first_of("0123456789");
    size_t end_pos = python_version_output.find_first_not_of("0123456789.", start_pos);
    std::string python_version = python_version_output.substr(start_pos, end_pos - start_pos);
    std::vector<std::string> parts = split_string(python_version, '.');

    std::ostringstream lib_name_stream;
    lib_name_stream << "libpython" << parts[0] << '.' << parts[1] << ".so";
    std::string lib_name = lib_name_stream.str();
    std::cout<<lib_name<<std::endl;
    void* handle = dlopen(lib_name.c_str(), RTLD_NOLOAD); // use already loaded python
    if(!handle && lib_name.back()=='o'){
        std::string  lib_name_so1 = lib_name+".1";
        handle = dlopen(lib_name_so1.c_str(), RTLD_NOLOAD);
    }

    if (!handle) {
        std::cerr << "Failed to load Python library" << std::endl;
        //return 1;
    }

    auto PyRun_SimpleString = (PyRun_SimpleString_t)dlsym(handle, "PyRun_SimpleString");
    auto PyPyGILState_Ensure = (PyGILState_Ensure_t)dlsym(handle, "PyGILState_Ensure");
    auto PyGILState_Release = (PyGILState_Release_t)dlsym(handle, "PyGILState_Release");

    if (!PyRun_SimpleString || !PyPyGILState_Ensure || !PyGILState_Release) {
        std::cerr << "Failed to resolve Python functions" << std::endl;
        dlclose(handle);
    }
    std::cout<<"Debug"<<std::endl;
    auto state = PyPyGILState_Ensure(); // acquire GIL
    PyRun_SimpleString(program.c_str());
    PyGILState_Release(state); // release GIL
    if (handle)
    { dlclose(handle); }
}
print('hello')
Running Engine
libpython3.10.so
Failed to load Python library
Debug
hello

发生

Failed to load Python library
是因为主要的Python可执行文件不加载
libpython
,而是静态链接它,所以这些函数是从可执行文件加载的,而不是共享库,如果另一个版本的Python动态链接
libpython
那么一定会成功的。

© www.soinside.com 2019 - 2024. All rights reserved.