前一段时间使用公司内部某个依赖共享内存的组件,其go版本api通过cgo提供。抛开我是个pure go狂热分子以外,采用cgo的方式实现会存在很多问题。所以分析其源码后通过go进行重写,故在此分享一下直接通过golang去操作共享内存。
通过golang操作共享内存主要依赖了以下几个系统调用:
- SYS_SHMGET
- SYS_SHMCTL
- SYS_SHMAT
- SYS_SHMDT
操作步骤:
1.根据shm key获取/创建共享内存:
shmid, _, err = syscall.Syscall(syscall.SYS_SHMGET, uintptr(key), uintptr(size), uintptr(mode&flags))
if err != 0 {
return nil, errors.New(err.Error())
}
2.获取共享内存状态:
//from bits/ipc.h
type shmid_ds struct {
ipc_perm struct {
key uint32
uid uint32
gid uint32
cuid uint32
cgid uint32
mode uint32
pad1 uint16
seq uint16
pad2 uint16
unused1 uint
unused2 uint
}
shm_segsz uint32
shm_atime uint64
shm_dtime uint64
shm_ctime uint64
shm_cpid uint32
shm_lpid uint32
shm_nattch uint16
shm_unused uint16
shm_unused2 uintptr
shm_unused3 uintptr
}
shmid_ds = &shmid_ds{}
_, _, err = syscall.Syscall(syscall.SYS_SHMCTL, shmid, uintptr(IPC_STAT), uintptr(unsafe.Pointer(shmid_ds)))
if err != 0 {
return nil, errors.New(err.Error())
}
3.映射共享内存地址到当前进程空间
addr, _, err = syscall.Syscall(syscall.SYS_SHMAT, shmid, 0, 0)
if err != 0 {
return 0, errors.New(err.Error())
}
4.指针类型转换。完成该步骤后,操作的对象实际就是共享内存对象,需要自行控制data race。
attrs := (YourType)(unsafe.Pointer(uintptr(addr)))
5.将内存地址从当前进程空间分离,分离后对象将不可用
_, _, err = syscall.Syscall(syscall.SYS_SHMDT, addr, 0, 0)
if err != 0 {
return errors.New(err.Error())
}