数组中未映射的内存访问从python传递到c++

unmapped memory access in array passed from python to c++

本文关键字:python c++ 访问 映射 内存 数组      更新时间:2023-10-16

我正在使用pybind11向python公开一个C++类。

它在构造函数中获取一个numpy.array,并获取一个指向其内部数据的指针。(它不复制数据(。

#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
#include <iostream>
namespace py = pybind11;
struct Data
{
Data(const py::array_t<double, py::array::c_style| py::array::forcecast>& arr)
: p(arr.data())
{
std::cout << "arr=" << p    << std::endl;
std::cout << "[0]=" << p[0] << std::endl;
}
const double* p;
};

我有另一个类,它接受const Data&,从而获得对数组数据的访问权限。

struct Manager
{
Manager(const Data& data)
: data_(data)
{
const double* p = data_.p;
std::cout << "data.arr=" << p    << std::endl;
std::cout << "data.[0]=" << p[0] << std::endl;
}
const Data& data_;
};

在这里,这两个类使用pybind11:向python公开

PYBIND11_MODULE(foo, m)
{
py::class_<Data>(m, "Data")
.def(py::init<const py::array_t<double, py::array::c_style| py::array::forcecast>&>());
py::class_<Manager>(m, "Manager")
.def(py::init<const Data&>());
}

这很有效。我可以导入我的模块,从numpy.array创建一个Data实例,然后将其传递给Manager:

>>> import pandas
>>> import numpy
>>> import foo
>>> df = pandas.DataFrame(data = numpy.random.rand(990000, 7))
>>> d = foo.Data(df.values)
>>> c = foo.Manager(d)

我的脚本运行良好,您可以看到我的C++代码访问numpy.array数据,并将其地址和第一个元素打印到stdout:

arr=0x7f47df313010
[0]=0.980507
data.arr=0x7f47df313010
data.[0]=0.980507

以上所有内容我都是为了创建一个MCVE来说明我在下面遇到的问题。

然而,现在,我加载了一个pandas DataFrame pickle文件,我有这个文件(这里是有问题的pickle的下载链接(:

>>> import pandas
>>> import foo
>>> df = pandas.read_pickle('data5.pk') 
>>> a = df.values
>>> d = foo.Data(a)
>>> c = foo.Manager(d)

并且我的C++代码在试图访问数组数据时崩溃。

这是标准输出:

arr=0x7f8864241010
arr[0]=7440.7
data.arr=0x7f8864241010
<dumps core>

因此,指向数组的指针在Manager中是相同的,但试图取消引用指针会导致SEGV。

通过valgrind运行它,valgrind报告Access not within mapped region at address 0x7f8864241010(即:numpy.array的地址(。

Python对我的pickle文件非常满意:

>>> import pandas
>>> df = pandas.read_pickle('data5.pk')
>>> df.shape
(990000, 7) 
D_11

我一辈子都无法弄清楚我的pickle文件出了什么问题。

  • 我试过创建一个numpy.array和酸洗,效果很好
  • 我试过制作pandas.DataFrame和酸洗,效果很好
  • 我已经将我的"无效"数据帧切片,我可以得到一个工作正常的子集

python对我的数据中的某些内容感到满意,但在C++中会导致SEGV。

我该如何诊断?

泡菜很好。是你的代码错了。您获取一个指向数组数据的指针,而不做任何事情来确保该数据实际上与使用它的对象一样长

您需要保留对数组的引用,并执行相关的refcount管理。pybind11可能有某种机制来表示Python引用并为您处理引用计数。快速查看文档,您的代码可能应该按值取array_t,而不是const引用(因为array_t已经表示Python引用(,并将其存储到array_t实例变量中。