Golang中如何使用runtime.Pinner固定map

Golang中如何使用runtime.Pinner固定map param 的内存来自 Mmap,因此直接为其成员赋值会导致问题。

func InitNullMap(param *tree.ExternParam, ctx CompilerContext) error {
	m := make(map[string][]string)
	param.NullMap = m

// write of unpinned Go pointer 0x1400cac1ce0 to non-Go memory 0x10ed44110
// fatal error: unpinned Go pointer stored into non-Go memory

我已经成功地使用 runtime.Pinner 处理了其他常见的数据类型,例如结构体或字符串,但我发现无法处理 map。

  m := make(map[string][]string)
  buf := ctx.GetBuffer()
  buf.Pin(m)
  param.NullMap = m

// what buf is and what buf.Pin do
func New() *Buffer {
	b := new(Buffer)
	b.pinner = new(runtime.Pinner)
	b.pinner.Pin(b)
	return b
}

func (b *Buffer) Pin(os ...any) {
	for _, o := range os {
		b.pinner.Pin(o)
	}
}

go/src/runtime/pinner_test.go 中没有相关示例,我也没有找到任何有用的讨论,有人能帮忙吗?


更多关于Golang中如何使用runtime.Pinner固定map的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang中如何使用runtime.Pinner固定map的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go语言中,runtime.Pinner 主要用于固定Go指针,防止它们被垃圾回收器移动,特别是当这些指针需要传递给非Go代码(如C代码)时。然而,对于map类型,直接使用 runtime.Pinner 固定可能不会按预期工作,因为map的内部结构包含指向堆内存的指针,而这些指针可能无法通过 Pinner 完全固定。

根据你的错误信息,问题在于尝试将一个未固定的Go指针存储到非Go内存中。这通常发生在将Go对象(如map)赋值给通过 Mmap 分配的内存区域时。runtime.Pinner 可以固定Go对象本身,但map内部的指针(如桶数组)可能未被固定,导致垃圾回收器移动这些内部数据,从而引发错误。

以下是一个示例,展示如何使用 runtime.Pinner 尝试固定map,但请注意,这并不能完全保证解决你的问题,因为map的内部指针可能仍然未被固定:

package main

import (
    "runtime"
    "unsafe"
)

func main() {
    // 创建一个map
    m := make(map[string][]string)
    m["key"] = []string{"value"}

    // 创建Pinner并尝试固定map
    pinner := runtime.Pinner{}
    pinner.Pin(m)

    // 获取map的指针(注意:这只是map头部的指针,内部指针可能未被固定)
    ptr := unsafe.Pointer(&m)

    // 在这里,你可以将ptr传递给非Go代码,但风险仍然存在
    // 因为map的内部结构(如桶)可能被垃圾回收器移动
    _ = ptr

    // 保持pinner存活,直到非Go代码不再需要map
    defer pinner.Unpin()
}

然而,对于你的具体场景,将map赋值给 param.NullMap(其中 param 的内存来自 Mmap),更可靠的方法是避免直接使用map,或者考虑使用其他数据表示形式。例如,你可以将map序列化为字节切片,然后存储到 Mmap 内存中。以下是一个示例:

func InitNullMap(param *tree.ExternParam, ctx CompilerContext) error {
    m := make(map[string][]string)
    // 假设你有序列化和反序列化map的函数
    data, err := serializeMap(m)
    if err != nil {
        return err
    }
    // 将数据复制到Mmap内存中
    // 假设param.NullMap是一个字节切片或类似的可存储数据的类型
    param.NullMap = data
    return nil
}

func serializeMap(m map[string][]string) ([]byte, error) {
    // 实现map的序列化,例如使用JSON、gob或自定义格式
    // 这里只是一个示例,你需要根据实际需求实现
    return []byte{}, nil
}

如果你必须使用map,并且 param.NullMap 是map类型,那么问题可能更复杂。在这种情况下,确保整个map(包括内部指针)不被垃圾回收器移动是困难的。你可能需要重新设计,避免将Go map存储到非Go内存中。

总之,直接使用 runtime.Pinner 固定map可能无法完全解决问题。建议考虑替代方案,如序列化或重新设计数据存储方式。

回到顶部