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

如何在cgo中复用共享库变量?Go生成so内存共享异常排查

问题原因

你误解了共享库的“共享”含义:Go编译的c-shared共享库仅实现代码段在多进程间复用,但每个进程加载共享库时,会为库中的全局变量创建独立的私有数据副本。也就是说,cgo1进程里初始化的globalConfig只存在于它自己的地址空间,cgo2进程加载同一个so后,会得到一个全新的、未初始化的globalConfig副本,自然输出空值。

解决方案:用系统级共享内存实现跨进程变量复用

要让多个进程共享同一份变量数据,必须依赖操作系统提供的跨进程共享内存机制(比如Linux的System V共享内存、mmap匿名共享内存等)。下面是基于Linux System V共享内存改造后的示例:

修改后的共享库代码(main.go)

package main

import "C"
import (
	"fmt"
	"os"
	"os/signal"
	"syscall"
	"time"
	"unsafe"
)

// 定义共享内存的标识key和存储大小
const (
	shmKey  = 0x12345678
	shmSize = 256 // 足够存储目标字符串的大小
)

var (
	globalConfig string
	shmAddr      unsafe.Pointer
)

func main() {}

//export search
func search() {
	if shmAddr == nil {
		// 挂载已存在的共享内存
		id, err := syscall.Shmget(shmKey, shmSize, 0666)
		if err != nil {
			fmt.Println("shmget error:", err)
			return
		}
		addr, err := syscall.Shmat(id, nil, 0)
		if err != nil {
			fmt.Println("shmat error:", err)
			return
		}
		shmAddr = addr
	}
	// 从共享内存读取字符串
	sharedStr := (*[shmSize]byte)(shmAddr)
	fmt.Println(string(sharedStr[:]))
}

//export start
func start() {
	// 创建或获取共享内存(不存在则创建)
	id, err := syscall.Shmget(shmKey, shmSize, 0666|syscall.IPC_CREAT)
	if err != nil {
		fmt.Println("shmget error:", err)
		return
	}
	// 挂载共享内存到当前进程地址空间
	addr, err := syscall.Shmat(id, nil, 0)
	if err != nil {
		fmt.Println("shmat error:", err)
		return
	}
	shmAddr = addr

	// 将全局变量写入共享内存
	globalConfig = "global value"
	copy((*[shmSize]byte)(addr)[:], globalConfig)

	// 保持进程运行
	go forever()

	quitChannel := make(chan os.Signal, 1)
	signal.Notify(quitChannel, syscall.SIGINT, syscall.SIGTERM)
	<-quitChannel

	// 进程退出时清理共享内存(可选,若需长期共享可删除此步骤)
	syscall.Shmdt(shmAddr)
	syscall.Shctl(id, syscall.IPC_RMID, nil)
}

func forever() {
	for {
		time.Sleep(10 * time.Second)
		fmt.Println(globalConfig)
	}
}

编译和运行步骤

  1. 重新编译共享库:
go build -buildmode=c-shared -o libsdk.so main.go
  1. 先启动cgo1.go(初始化共享内存):
go run cgo1.go
  1. 另开终端启动cgo2.go:
go run cgo2.go

此时cgo2会输出global value,实现了跨进程的变量复用。

额外说明
  • 共享内存是系统级资源,需注意生命周期管理:如果start函数退出时执行了IPC_RMID,其他进程将无法再访问该共享内存;若需要长期共享,可以去掉清理步骤,后续手动用ipcrm命令删除。
  • 除了System V共享内存,也可以用mmap映射文件实现共享,或者用Unix域套接字、Redis等中间件实现进程间数据共享,可根据实际场景选择。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 00:01:15