github.com/teepark/go-sysvipc@v0.0.0-20200817232735-d7ca6053ea29/shm.go (about) 1 package sysvipc 2 3 /* 4 #include <string.h> 5 #include <sys/ipc.h> 6 #include <sys/shm.h> 7 int shmget(key_t key, size_t size, int shmflg); 8 void *shmat(int shmid, const void *shmaddr, int shmflg); 9 int shmdt(const void *shmaddr); 10 int shmctl(int shmid, int cmd, struct shmid_ds *buf); 11 */ 12 import "C" 13 import ( 14 "errors" 15 "io" 16 "sync/atomic" 17 "time" 18 "unsafe" 19 ) 20 21 var ( 22 ErrReadOnlyShm = errors.New("Read-Only shared mem attachment") 23 ) 24 25 // SharedMem is an allocated block of memory sharable with multiple processes. 26 type SharedMem struct { 27 id int64 28 length uint 29 } 30 31 // GetSharedMem creates or retrieves the shared memory segment for an IPC key 32 func GetSharedMem(key int64, size uint64, flags *SHMFlags) (*SharedMem, error) { 33 rc, err := C.shmget(C.key_t(key), C.size_t(size), C.int(flags.flags())) 34 if rc == -1 { 35 return nil, err 36 } 37 return &SharedMem{int64(rc), uint(size)}, nil 38 } 39 40 // Attach brings a shared memory segment into the current process's memory space. 41 func (shm *SharedMem) Attach(flags *SHMAttachFlags) (*SharedMemMount, error) { 42 ptr, err := C.shmat(C.int(shm.id), nil, C.int(flags.flags())) 43 if err != nil { 44 return nil, err 45 } 46 47 return &SharedMemMount{ptr, 0, shm.length, flags.ro()}, nil 48 } 49 50 // Stat produces meta information about the shared memory segment. 51 func (shm *SharedMem) Stat() (*SHMInfo, error) { 52 shmds := C.struct_shmid_ds{} 53 54 rc, err := C.shmctl(C.int(shm.id), C.IPC_STAT, &shmds) 55 if rc == -1 { 56 return nil, err 57 } 58 59 shminf := SHMInfo{ 60 Perms: IpcPerms{ 61 OwnerUID: int(shmds.shm_perm.uid), 62 OwnerGID: int(shmds.shm_perm.gid), 63 CreatorUID: int(shmds.shm_perm.cuid), 64 CreatorGID: int(shmds.shm_perm.cgid), 65 Mode: uint16(shmds.shm_perm.mode), 66 }, 67 SegmentSize: uint(shmds.shm_segsz), 68 LastAttach: time.Unix(int64(shmds.shm_atime), 0), 69 LastDetach: time.Unix(int64(shmds.shm_dtime), 0), 70 LastChange: time.Unix(int64(shmds.shm_ctime), 0), 71 CreatorPID: int(shmds.shm_cpid), 72 LastUserPID: int(shmds.shm_lpid), 73 CurrentAttaches: uint(shmds.shm_nattch), 74 } 75 76 return &shminf, nil 77 } 78 79 // Set updates parameters of the shared memory segment. 80 func (shm *SharedMem) Set(info *SHMInfo) error { 81 shmds := &C.struct_shmid_ds{ 82 shm_perm: C.struct_ipc_perm{ 83 uid: C.__uid_t(info.Perms.OwnerUID), 84 gid: C.__gid_t(info.Perms.OwnerGID), 85 mode: C.ushort(info.Perms.Mode & 0x1FF), 86 }, 87 } 88 89 rc, err := C.shmctl(C.int(shm.id), C.IPC_SET, shmds) 90 if rc == -1 { 91 return err 92 } 93 return nil 94 } 95 96 // Remove marks the shared memory segment for removal. 97 // It will be removed when all attachments have been closed. 98 func (shm *SharedMem) Remove() error { 99 rc, err := C.shmctl(C.int(shm.id), C.IPC_RMID, nil) 100 if rc == -1 { 101 return err 102 } 103 return nil 104 } 105 106 // SharedMemMount is the pointer to an attached block of shared memory space. 107 type SharedMemMount struct { 108 ptr unsafe.Pointer 109 offset, length uint 110 111 // We have to store readonly here to prevent Write and WriteByte. 112 // I'd be happy to let it panic, but C segfault panics can't recover. 113 readonly bool 114 } 115 116 // Read pulls bytes out of the shared memory segment. 117 func (shma *SharedMemMount) Read(p []byte) (int, error) { 118 var err error 119 l := uint(len(p)) 120 if l > (shma.length - shma.offset) { 121 l = shma.length - shma.offset 122 err = io.EOF 123 } 124 if l == 0 { 125 return 0, err 126 } 127 128 src := unsafe.Pointer(uintptr(shma.ptr) + uintptr(shma.offset)) 129 dest := unsafe.Pointer(&p[0]) 130 131 memmove(dest, src, (uintptr)(l)) 132 shma.offset += l 133 return int(l), err 134 } 135 136 // Write places bytes into the shared memory segment. 137 func (shma *SharedMemMount) Write(p []byte) (int, error) { 138 if shma.readonly { 139 // see comment on readonly field above 140 return 0, ErrReadOnlyShm 141 } 142 143 var err error 144 l := uint(len(p)) 145 if l > (shma.length - shma.offset) { 146 l = shma.length - shma.offset 147 err = io.ErrShortWrite 148 } 149 if l == 0 { 150 return 0, err 151 } 152 153 dest := unsafe.Pointer(uintptr(shma.ptr) + uintptr(shma.offset)) 154 src := unsafe.Pointer(&p[0]) 155 156 memmove(dest, src, (uintptr)(l)) 157 shma.offset += l 158 return int(l), err 159 } 160 161 // AtomicWriteUint32 places an uint32 value into the shared memory 162 // segment atomically (see "sync/atomic"). 163 func (shma *SharedMemMount) AtomicWriteUint32(v uint32) error { 164 if shma.readonly { 165 // see comment on readonly field above 166 return ErrReadOnlyShm 167 } 168 169 if (shma.length - shma.offset) < 4 { 170 return io.ErrShortWrite 171 } 172 173 atomic.StoreUint32((*uint32)((unsafe.Pointer)(uintptr(shma.ptr)+uintptr(shma.offset))), v) 174 shma.offset += 4 175 return nil 176 } 177 178 // AtomicReadUint32 returns an uint32 value from the current position 179 // of the shared memory segment using atomic read (see "sync/atomic"). 180 func (shma *SharedMemMount) AtomicReadUint32() (uint32, error) { 181 if (shma.length - shma.offset) < 4 { 182 return 0, io.EOF 183 } 184 185 v := atomic.LoadUint32((*uint32)((unsafe.Pointer)(uintptr(shma.ptr) + uintptr(shma.offset)))) 186 shma.offset += 4 187 return v, nil 188 } 189 190 // ReadByte returns a single byte from the current position in shared memory. 191 func (shma *SharedMemMount) ReadByte() (byte, error) { 192 if shma.offset == shma.length { 193 return 0, io.EOF 194 } 195 b := *(*byte)(unsafe.Pointer(uintptr(shma.ptr) + uintptr(shma.offset))) 196 shma.offset++ 197 return b, nil 198 } 199 200 // UnreadByte sets the position back to before a ReadByte. 201 func (shma *SharedMemMount) UnreadByte() error { 202 if shma.offset == 0 { 203 return errors.New("sysvipc: UnreadByte before any ReadByte") 204 } 205 shma.offset-- 206 return nil 207 } 208 209 // WriteByte places a single byte at the current position in shared memory. 210 func (shma *SharedMemMount) WriteByte(c byte) error { 211 if shma.readonly { 212 // see comment on readonly field above 213 return ErrReadOnlyShm 214 } 215 if shma.offset == shma.length { 216 return io.ErrShortWrite 217 } 218 b := (*byte)(unsafe.Pointer(uintptr(shma.ptr) + uintptr(shma.offset))) 219 *b = c 220 shma.offset++ 221 return nil 222 } 223 224 // Seek moves the current position in shared memory, according to "whence": 225 // - 0 makes the offset relative to the beginning 226 // - 1 makes it relative to the current position 227 // - 2 makes it relative to the end of the segment 228 func (shma *SharedMemMount) Seek(offset int64, whence int) (int64, error) { 229 var endpos int64 230 switch whence { 231 case 0: 232 endpos = offset 233 case 1: 234 endpos = int64(shma.offset) + offset 235 case 2: 236 endpos = int64(shma.length) + offset 237 default: 238 return 0, errors.New("sysvipc: bad 'whence' value") 239 } 240 241 if endpos < 0 { 242 return int64(shma.offset), errors.New("sysvipc: negative offset") 243 } 244 245 if uint(endpos) > shma.length { 246 shma.offset = shma.length 247 } else { 248 shma.offset = uint(endpos) 249 } 250 251 return int64(shma.offset), nil 252 } 253 254 // Close detaches the shared memory segment pointer. 255 func (shma *SharedMemMount) Close() error { 256 rc, err := C.shmdt(shma.ptr) 257 if rc == -1 { 258 return err 259 } 260 return nil 261 } 262 263 // SHMInfo holds meta information about a shared memory segment. 264 type SHMInfo struct { 265 Perms IpcPerms 266 SegmentSize uint 267 268 LastAttach time.Time 269 LastDetach time.Time 270 LastChange time.Time 271 272 CreatorPID int 273 LastUserPID int 274 275 CurrentAttaches uint 276 } 277 278 // SHMFlags holds the options for GetSharedMem 279 type SHMFlags struct { 280 // Create controls whether to create the shared memory segment if it 281 // doesn't already exist. 282 Create bool 283 284 // Exclusive causes GetSharedMem to fail if the shared memory already 285 // exists (only useful with Create). 286 Exclusive bool 287 288 // Perms is the file-style (rwxrwxrwx) permissions with which to create the 289 // shared memory segment (also only useful with Create). 290 Perms int 291 } 292 293 func (sf *SHMFlags) flags() int64 { 294 if sf == nil { 295 return 0 296 } 297 298 var f int64 = int64(sf.Perms) & 0777 299 if sf.Create { 300 f |= int64(C.IPC_CREAT) 301 } 302 if sf.Exclusive { 303 f |= int64(C.IPC_EXCL) 304 } 305 306 return f 307 } 308 309 // SHMAttachFlags holds the options for SharedMem.Attach 310 type SHMAttachFlags struct { 311 // ReadOnly causes the new SharedMemMount to be readable but not writable 312 ReadOnly bool 313 } 314 315 func (sf *SHMAttachFlags) flags() int64 { 316 if sf == nil { 317 return 0 318 } 319 320 var f int64 321 if sf.ReadOnly { 322 f |= int64(C.SHM_RDONLY) 323 } 324 325 return f 326 } 327 328 func (sf *SHMAttachFlags) ro() bool { 329 if sf == nil { 330 return false 331 } 332 return sf.ReadOnly 333 }