You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ubuntu下C++调用Cython编译的.so中Python函数遇段错误求助

问题:C++调用Cython编译的Python模块出现段错误

环境与准备工作

  • 系统:Ubuntu
  • Python环境:Miniconda Python3.9
  • 工作目录:~/exp,目录结构:
    exp
    ├── exp
    │   ├── __init__.py
    │   └── main.pyx
    ├── setup.py
    ├── run.cpp
    └── run.py
    

Cython模块代码(exp/main.pyx)

def add(a, b):
    return a+b

def entry_point():
    print(add(21,21))

编译脚本(setup.py)

from setuptools import setup
from Cython.Build import cythonize

setup(
   name='exp',
   ext_modules=cythonize("exp/main.pyx"),
   libraries=[('python3.9', {'include_dirs': ["~/miniconda3/include/python3.9"]})],
   library_dirs=['~/miniconda3/lib']
)

编译命令与验证

执行编译生成.so文件:

python3 setup.py build_ext --inplace

生成的main.cpython-39-x86_64-linux-gnu.so位于~/exp/exp目录下。

使用Python调用正常(run.py):

from exp.main import entry_point

if __name__ == "__main__":
    entry_point()

执行后输出42,模块功能正常。

问题重现

编写run.cpp尝试调用Cython模块:

#include <iostream>
#include <dlfcn.h>
#include "Python.h"

int main(int argc, char *argv[]) {
   setenv("PYTHONPATH",".",1);
   Py_Initialize();
   PyObject *pName, *pModule, *pDict, *pFunc, *pValue, *presult;
   // Initialize the Python Interpreter
   Py_Initialize();

   // Build the name object
   pName = PyUnicode_FromString((char*)"exp/main");

   // Load the module object
   pModule = PyImport_Import(pName);

   // pDict is a borrowed reference 
   pDict = PyModule_GetDict(pModule);

   // pFunc is also a borrowed reference 
   pFunc = PyDict_GetItemString(pDict, (char*)"entry_point");

   Py_DECREF(pValue);

   // Clean up
   Py_DECREF(pModule);
   Py_DECREF(pName);
   Py_Finalize();
}

编译命令:

g++ -Wall -I~/miniconda3/include/python3.9 run.cpp -L~/miniconda3/lib -lpython3.9 -o run.o -ldl

执行./run.o时出现Segmentation fault (core dumped)。

错误分析与修复步骤

1. 重复初始化Python解释器

代码中连续调用了两次Py_Initialize(),会导致Python内部状态异常,直接删除其中一次即可。

2. 未检查空指针

PyImport_Import(pName)可能因模块找不到、路径错误等返回NULL,后续直接操作pModule会触发段错误,必须添加空指针检查:

if (pModule == NULL) {
    PyErr_Print();
    std::cerr << "Failed to import module" << std::endl;
    return 1;
}

3. 未初始化的指针调用Py_DECREF

pValue未被赋值就调用Py_DECREF(pValue),会访问非法内存地址,直接删除这行代码。

4. 模块导入名称错误

Python模块导入应该使用点分隔符(exp.main)而非斜杠(exp/main),修正模块名称:

pName = PyUnicode_FromString("exp.main");

5. 编译命令的路径解析问题

g++参数中的~不会被shell解析,需替换为绝对路径或使用$HOME展开:

g++ -Wall -I$HOME/miniconda3/include/python3.9 run.cpp -L$HOME/miniconda3/lib -lpython3.9 -o run -ldl

(注意:输出文件名建议用run而非run.o,避免和目标文件混淆)

6. 缺少函数调用与合法性检查

找到pFunc后,需要检查它是否是可调用对象,再执行调用:

if (PyCallable_Check(pFunc)) {
    presult = PyObject_CallObject(pFunc, NULL);
    if (presult == NULL) {
        PyErr_Print();
    }
    Py_XDECREF(presult);
} else {
    std::cerr << "entry_point is not callable" << std::endl;
    PyErr_Print();
}

修复后的完整run.cpp代码

#include <iostream>
#include <dlfcn.h>
#include "Python.h"

int main(int argc, char *argv[]) {
    setenv("PYTHONPATH", ".", 1);
    // 仅初始化一次Python解释器
    if (!Py_Initialize()) {
        std::cerr << "Failed to initialize Python interpreter" << std::endl;
        return 1;
    }

    PyObject *pName = PyUnicode_FromString("exp.main");
    if (pName == NULL) {
        PyErr_Print();
        Py_Finalize();
        return 1;
    }

    PyObject *pModule = PyImport_Import(pName);
    if (pModule == NULL) {
        PyErr_Print();
        Py_DECREF(pName);
        Py_Finalize();
        return 1;
    }

    PyObject *pDict = PyModule_GetDict(pModule);
    if (pDict == NULL) {
        PyErr_Print();
        Py_DECREF(pModule);
        Py_DECREF(pName);
        Py_Finalize();
        return 1;
    }

    PyObject *pFunc = PyDict_GetItemString(pDict, "entry_point");
    if (pFunc != NULL && PyCallable_Check(pFunc)) {
        PyObject *presult = PyObject_CallObject(pFunc, NULL);
        if (presult == NULL) {
            PyErr_Print();
        }
        Py_XDECREF(presult);
    } else {
        if (PyErr_Occurred()) {
            PyErr_Print();
        } else {
            std::cerr << "Could not find callable entry_point function" << std::endl;
        }
    }

    // 清理资源
    Py_DECREF(pModule);
    Py_DECREF(pName);
    Py_Finalize();
    return 0;
}

测试验证

  1. 重新编译C++程序:
g++ -Wall -I$HOME/miniconda3/include/python3.9 run.cpp -L$HOME/miniconda3/lib -lpython3.9 -o run -ldl
  1. 执行程序:
./run

此时应输出42,无段错误。

内容的提问来源于stack exchange,提问作者Guitheg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 02:50:27