【问题标题】:Go pointer stored into non-Go memory存储在非 Go 内存中的 Go 指针
【发布时间】:2021-04-10 05:11:15
【问题描述】:

我有一个新手 CGO 问题,我想知道是否有人可以帮助我。当 GODEBUG 设置为 cgocheck=2 运行时,我的应用程序崩溃并显示以下内容

write of Go pointer 0xc0003b72c0 to non-Go memory 0x7fefa0016810
fatal error: Go pointer stored into non-Go memory

导致问题的代码是

cArray := C.malloc(C.size_t(len(fd.Faces)) * C.size_t(unsafe.Sizeof(uintptr(0))))
defer C.free(unsafe.Pointer(cArray))
a := (*[1<<30 - 1]*C.struct_Box)(cArray)
for index, value := range fd.GetFaceRectangles() {
    box := &C.struct_Box{
        left: C.int(value.Min.X),
        top: C.int(value.Min.Y),
        right: C.int(value.Max.X),
        bottom: C.int(value.Max.Y),
    }
    a[index] = box
}
cBoxCount := C.int(len(fd.Faces))
ret := C.facerec_compute_multi(rec.ptr, cImgData, cLen,  &a[0], cBoxCount)

特别是这一行:

a[index] = box

我了解数组的内存是在 C 中使用 malloc 分配的。我正在尝试将 C Box 添加到数组中,然后再将其传递给 C 函数。解决这个问题的方法是让我在 C 中编写一个函数,该函数可以接收数组和创建结构所需的项目,而我在 C 中执行该部分吗?我试图最小化对 C 的调用次数,所以如果我可以从 Go 创建数组,那就太好了。真的很苦恼如何安全地将一组数据传递给 C 中的函数。

【问题讨论】:

  • 不确定我是否可以制定答案,因为这对我来说很难测试,但这是我的想法:1) 尝试将 box 转换为 unsafe.Pointer 甚至 uintptr在将其分配给a[index] 之前。 2) 您也许还可以在 Go 中构建整个数组,然后将其转换为 uintptr 并将其传递给 C。
  • 如果你还没有读过这篇文章:go101.org/article/unsafe.html

标签: go cgo


【解决方案1】:

您已分配 C 内存来保存许多单独的指针。每个单独的指针都可以设置为单独的值。就目前而言这很好,但正如您所指出的,a[index] = box 是一个问题,因为box 本身拥有一个指向 Go 内存 C.struct_Box 的指针。

我正在尝试将 C Box 添加到数组中,然后再将其传递给 C 函数。

要做到这一点,你需要一个C.malloc 电话。正如所写,每个C.struct_Box 实例都需要一个。

我是否可以在 C 中编写一个函数来解决此问题,该函数可以接收数组和创建结构所需的项目,而我在 C 中执行该部分?

虽然您可以尝试这样做,但它似乎......不是最理想的。

我正在尝试尽量减少对 C 的调用次数,所以如果我可以从 Go 创建数组,那就太好了。真的很苦恼如何安全地将数据数组传递给 C 中的函数。

C 从来都不是真正的安全 ? 但我怀疑你最好的选择是利用 C 实现“数组”的方式,这是一种穷人的切片。 (或者,也许更准确地说,Go 切片是正确的“C 数组”。)请记住,切片在 Go 中实现为三元素标题:

  • 指向存储区底部的指针;
  • 存储区域内的当前长度;和
  • 存储区容量

然后可以保存一些运行时计算出的某种类型的项目数n。 C 的动态“数组”以相同的方式工作,只是没有适当的切片头的安全性。

这意味着你可以编写你的 C 代码——提供你有这个 C 代码的控制——获取一个指向存储区基址的指针,和一个 intsize_t 值,用于计算该区域中项目的数量 n。这样一个函数的签名是:

void f(T *base, size_t n);

其中n 是项目数。 (如果合适,添加第二个size_t,如果您还想提供容量,但如果内存有容量,并且函数将其修改为包含不同数量的项目,那么函数必须返回新的数量项目——即f 不会是void——或者n 本身必须通过引用或类似的方式提供。)

在这种情况下,如果您可以安排 C 代码获取指向nstruct Box-es 的第一个的指针(而不是n 的第一个@指针到 @987654337 @-es),您可以在单个 C.malloc 调用中分配它。

如果您无法重新编写 C 代码,那么您至少会遇到两个 C.malloc 调用。但是,您可以再次将所有struct Boxes 分配到一个C.malloc 中,并使它们全部相邻——即,一个C“数组”/穷人切片——然后使每个指针指向下一个@987654342 @:

cArray := C.malloc(C.size_t(len(fd.Faces)) * C.size_t(unsafe.Sizeof(uintptr(0))))
defer C.free(unsafe.Pointer(cArray))
cBoxes := C.malloc(C.size_t(len(fd.Faces)) * C.size_t(unsafe.Sizeof(C.struct_Box)))
defer C.free(unsafe.Pointer(cBoxes))
a := (*[1<<30 - 1]*C.struct_Box)(cArray)
b := (*[1<<30 - 1]C.struct_Box)(cBoxes)
for index, value := range fd.GetFaceRectangles() {
    b[index] = C.struct_Box{
        left: C.int(value.Min.X),
        top: C.int(value.Min.Y),
        right: C.int(value.Max.X),
        bottom: C.int(value.Max.Y),
    }
    a[index] = &b[index]
}
cBoxCount := C.int(len(fd.Faces))
ret := C.facerec_compute_multi(rec.ptr, cImgData, cLen,  &a[0], cBoxCount)

(我试图在这里尽可能地保持代码结构相似,这是未经测试的,因为我没有某些部分。)

除此之外:您有len(fd.Faces)fd.GetFaceRectangles(),这里的代码假定for ... range fd.GetFaceRectangles() 的迭代次数始终为len(fd.Faces)。我之所以这么说是因为我做了同样的假设,除了你的例子之外没有任何支持。

【讨论】:

    【解决方案2】:

    在 Slack 频道上的一些帮助下,我最终得到了这个,只需直接添加结构而不是指针。

    // Create an array in C
    arr := C.malloc(C.size_t(len(fd.Faces)) * C.sizeof_Box)
    defer C.free(unsafe.Pointer(arr))
    
    var boxes []C.struct_Box
    hdr := (*reflect.SliceHeader)(unsafe.Pointer(&boxes))
    hdr.Data, hdr.Cap, hdr.Len = uintptr(arr), len(fd.Faces), len(fd.Faces)
    
    for i, v := range fd.GetFaceRectangles() {
        boxes[i] = C.struct_Box{
            left: C.int(v.Min.X),
            top: C.int(v.Min.Y),
            right: C.int(v.Max.X),
            bottom: C.int(v.Max.Y),
        }
    }
    cBoxCount := C.int(len(fd.Faces))
    ret := C.facerec_compute_multi(rec.ptr, cImgData, cLen, &(boxes[0]), cBoxCount)
    

    【讨论】:

    • 仅供参考,这或多或少是我推荐的想法:制作一个 C“盒子”结构的线性数组并填充它们。(如果可能的话,我也会避免使用 reflect.SliceHeader 技巧,但它是很好。另一个构造有那个奇怪的[1&lt;&lt;30] 或类似的技巧,所以它同样丑陋。)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-07-12
    • 1970-01-01
    • 2020-09-23
    • 2016-08-15
    • 1970-01-01
    • 2017-01-16
    • 2015-12-18
    相关资源
    最近更新 更多