Golang 共享内存的使用

本文介绍了如何在Golang中使用系统调用创建和挂载共享内存,以及处理内存大小不一致和共享内存管理的方法,包括查询、删除操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是共享内存

共享内存是一种计算机系统中用于多个进程或线程之间共享数据的技术或机制。在共享内存中,多个进程或线程可以访问同一块内存区域,从而实现它们之间的数据共享。这使得进程或线程之间可以直接读取和写入共享内存中的数据,而无需通过其他通信机制(如管道、消息队列等)来进行数据传输。
通俗理解,共享内存,好似一款用内存做的磁盘,不同进程都可以访问和写入,因为通过系统API构建的一块内存空间,不属于任何一个进程,则自己的进程退出,数据依然保持。(由于是内存,系统重启数据当然会丢失)

Golang 创建并挂载共享内存


import (
	"unsafe"
)

func bindShareMemory() unsafe.Pointer {
	// 声明需要创建共享内存的大小
	const memory_size = 2 * 1024 * 1024 // 这里 2M
	var shareKey uintptr = 3 // 自己定义共享内存的key
	var readOnly = false // 是否只读方式挂载
	var address uintptr = 0 // 共享内存地址,为0即可
	// 通过KEY, 获取一个共享内存,如果不存在会创建
	shmId, _, err := syscall.Syscall(syscall.SYS_SHMGET, shareKey, memory_size, 00001000|0600) // shmId 为共享内存的ID(操作系统构建返回
	
	if err != 0 {
		panic(err)
	}
	
	flags :=  uintptr(0x0)
	if readOnly {
		flags = uintptr(0x00001000) // 只读挂载
	}
	
	// 开始挂载共享内存, shmAddress为共享内存地址
	shmAddress, _, err2 := syscall.Syscall(syscall.SYS_SHMAT, shmId, address, flags)
	if err2 != 0 {
		panic(err2)
	}
	
	return unsafe.Pointer(shmAddress)
}

写入一个int类型的值,测试一下


func main() {
	pointer := bindShareMemory()
	lastInt := *(*int)(pointer)
	fmt.Printf("上一次写入数值: %v \n", lastInt)
	value := rand.Intn(12454123)
	fmt.Printf("这次写入数值: %v \n", value)
	*(*int)(pointer) = value // 先转换成 int指针在 解引用,这样对该内存块赋值
}

问题解答

  • 如果 操作系统已经存在了某个相同 shareKey, 但是memory_size 不同的情况,挂载会 异常,
    也就是 shmId, _, err := syscall.Syscall(syscall.SYS_SHMGET, shareKey, memory_size, 00001000|0600)这里的 err 将不会返回 0 值, 解决办法,需要删除操作系统之前的共享内存

共享内存相关 操作

  • 查询操作系统的共享内存
ipcs -m
  • 删除共享内存
ipcrm -m 196608
# 196608 为 shared memory ID
### Golang 实现共享内存 在 Go 中实现共享内存可以通过 `syscall` 或者第三方库如 `github.com/edsrzf/mmap-go` 来完成。下面展示一种基于 `syscall.Mmap` 的方式来创建和访问共享内存区。 #### 创建共享内存 (Writer) ```go package main import ( "fmt" "os" "syscall" "unsafe" ) type SharedData struct { Message string } func createSharedMemory(filename string, size int) (*os.File, []byte, error) { file, err := os.OpenFile(filename, os.O_CREATE|os.O_RDWR, 0666) if err != nil { return nil, nil, fmt.Errorf("open file failed: %v", err) } err = syscall.Ftruncate(int(file.Fd()), int64(size)) if err != nil { return nil, nil, fmt.Errorf("truncating file to length %d failed: %v", size, err) } memoryRegion, err := syscall.Mmap(int(file.Fd()), 0, size, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED) if err != nil { return nil, nil, fmt.Errorf("mmap call failed: %v", err) } return file, memoryRegion, nil } func writeSharedMemory(memory []byte, data *SharedData) error { strPtr := (*reflect.StringHeader)(unsafe.Pointer(&data.Message)) copy(memory[:strPtr.Len], data.Message) return nil } func main() { const SHARED_MEMORY_SIZE = 4 << 30 // 4GB var sharedData = &SharedData{Message: "Hello from writer"} file, mem, err := createSharedMemory("/tmp/sharedmem.dat", SHARED_MEMORY_SIZE) defer func() { syscall.Munmap(mem) file.Close() }() if err != nil { fmt.Println(err) os.Exit(1) } writeSharedMemory(mem, sharedData) fmt.Printf("Wrote '%s' into shared memory\n", sharedData.Message) } ``` 此代码片段展示了如何设置一块共享内存,并向其中写入字符串消息[^1]。 #### 访问共享内存 (Reader) ```go package main import ( "fmt" "os" "syscall" ) type SharedData struct { Message string } func readFromSharedMemory(memory []byte) string { messageLen := bytes.IndexByte(memory, 0) if messageLen == -1 { messageLen = len(memory) } return string(memory[:messageLen]) } func openExistingSharedMemory(filename string) ([]byte, error) { file, err := os.OpenFile(filename, os.O_RDONLY, 0666) if err != nil { return nil, fmt.Errorf("opening existing shm file failed: %v", err) } info, _ := file.Stat() size := info.Size() mappedMem, err := syscall.Mmap(int(file.Fd()), 0, int(size), syscall.PROT_READ, syscall.MAP_SHARED) if err != nil { return nil, fmt.Errorf("mapping existing shm region failed: %v", err) } return mappedMem, nil } func main() { mem, err := openExistingSharedMemory("/tmp/sharedmem.dat") defer syscall.Munmap(mem) if err != nil { fmt.Println(err) os.Exit(1) } dataRead := readFromSharedMemory(mem) fmt.Printf("Read '%s' from shared memory\n", dataRead) } ``` 上述例子中,读者进程打开同一个文件并将其映射到自己的地址空间内,从而可以读取由另一个进程所写的相同位置的数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值