github.com/scottcagno/storage@v1.8.0/pkg/mmap/mmap_linux_amd64.go (about) 1 package mmap 2 3 import ( 4 "os" 5 "reflect" 6 "runtime" 7 "syscall" 8 "unsafe" 9 ) 10 11 // Mapping is a mapping of the file into the memory. 12 type Mapping struct { 13 mapping 14 // alignedAddress specifies the start address of the the mapped memory 15 // aligned by the memory page size. 16 alignedAddress uintptr 17 // alignedLength specifies the length of the mapped memory, in bytes, 18 // aligned by the memory page size. 19 alignedLength uintptr 20 // locked specifies whether the mapped memory is locked. 21 locked bool 22 } 23 24 // Open opens and returns a new mapping of the given file into the memory. 25 // The given file descriptor will be duplicated. It means that 26 // if the parent file will be closed the mapping will still be valid. 27 // Actual offset and length may be different than the given 28 // by the reason of aligning to the memory page size. 29 func Open(fd uintptr, offset int64, length uintptr, mode Mode, flags Flag) (*Mapping, error) { 30 31 // Using int64 (off_t) for the offset and uintptr (size_t) for the length 32 // by the reason of the compatibility. 33 if offset < 0 { 34 return nil, ErrBadOffset 35 } 36 if length > uintptr(MaxInt) { 37 return nil, ErrBadLength 38 } 39 40 m := &Mapping{} 41 prot := syscall.PROT_READ 42 mmapFlags := syscall.MAP_SHARED 43 if mode < ModeReadOnly || mode > ModeWriteCopy { 44 return nil, ErrBadMode 45 } 46 if mode > ModeReadOnly { 47 prot |= syscall.PROT_WRITE 48 m.writable = true 49 } 50 if mode == ModeWriteCopy { 51 flags = syscall.MAP_PRIVATE 52 } 53 if flags&FlagExecutable != 0 { 54 prot |= syscall.PROT_EXEC 55 m.executable = true 56 } 57 58 // The mapping address range must be aligned by the memory page size. 59 pageSize := int64(os.Getpagesize()) 60 if pageSize < 0 { 61 return nil, os.NewSyscallError("getpagesize", syscall.EINVAL) 62 } 63 outerOffset := offset / pageSize 64 innerOffset := offset % pageSize 65 // ASSERT: uintptr is of the 64-bit length on the amd64 architecture. 66 m.alignedLength = uintptr(innerOffset) + length 67 68 var err error 69 m.alignedAddress, err = mmap(0, m.alignedLength, prot, mmapFlags, fd, outerOffset) 70 if err != nil { 71 return nil, os.NewSyscallError("mmap", err) 72 } 73 m.address = m.alignedAddress + uintptr(innerOffset) 74 75 // Wrapping the mapped memory by the byte slice. 76 slice := reflect.SliceHeader{} 77 slice.Data = m.address 78 slice.Len = int(length) 79 slice.Cap = slice.Len 80 m.memory = *(*[]byte)(unsafe.Pointer(&slice)) 81 82 runtime.SetFinalizer(m, (*Mapping).Close) 83 return m, nil 84 } 85 86 // Lock locks the mapped memory pages. 87 // All pages that contain a part of the mapping address range 88 // are guaranteed to be resident in RAM when the call returns successfully. 89 // The pages are guaranteed to stay in RAM until later unlocked. 90 // It may need to increase process memory limits for operation success. 91 // See working set on Windows and rlimit on Linux for details. 92 func (m *Mapping) Lock() error { 93 if m.memory == nil { 94 return ErrClosed 95 } 96 if m.locked { 97 return ErrLocked 98 } 99 if err := mlock(m.alignedAddress, m.alignedLength); err != nil { 100 return os.NewSyscallError("mlock", err) 101 } 102 m.locked = true 103 return nil 104 } 105 106 // Unlock unlocks the previously locked mapped memory pages. 107 func (m *Mapping) Unlock() error { 108 if m.memory == nil { 109 return ErrClosed 110 } 111 if !m.locked { 112 return ErrNotLocked 113 } 114 if err := munlock(m.alignedAddress, m.alignedLength); err != nil { 115 return os.NewSyscallError("munlock", err) 116 } 117 m.locked = false 118 return nil 119 } 120 121 // Sync synchronizes the mapped memory with the underlying file. 122 func (m *Mapping) Sync() error { 123 if m.memory == nil { 124 return ErrClosed 125 } 126 if !m.writable { 127 return ErrReadOnly 128 } 129 return os.NewSyscallError("msync", msync(m.alignedAddress, m.alignedLength)) 130 } 131 132 // Close closes this mapping and frees all resources associated with it. 133 // Mapped memory will be synchronized with the underlying file and unlocked automatically. 134 // Close implements the io.Closer interface. 135 func (m *Mapping) Close() error { 136 if m.memory == nil { 137 return ErrClosed 138 } 139 var errs []error 140 141 // Maybe unnecessary. 142 if m.writable { 143 if err := m.Sync(); err != nil { 144 errs = append(errs, err) 145 } 146 } 147 if m.locked { 148 if err := m.Unlock(); err != nil { 149 errs = append(errs, err) 150 } 151 } 152 153 if err := munmap(m.alignedAddress, m.alignedLength); err != nil { 154 errs = append(errs, os.NewSyscallError("munmap", err)) 155 } 156 *m = Mapping{} 157 runtime.SetFinalizer(m, nil) 158 if len(errs) > 0 { 159 return errs[0] 160 } 161 return nil 162 } 163 164 // errno returns a system error code. 165 func errno(err error) error { 166 if err != nil { 167 if en, ok := err.(syscall.Errno); ok && en == 0 { 168 return syscall.EINVAL 169 } 170 return err 171 } 172 return syscall.EINVAL 173 } 174 175 // mmap wraps the system call for mmap. 176 func mmap(addr, length uintptr, prot, flags int, fd uintptr, offset int64) (uintptr, error) { 177 if prot < 0 || flags < 0 || offset < 0 { 178 return 0, syscall.EINVAL 179 } 180 result, _, err := syscall.Syscall6(syscall.SYS_MMAP, addr, length, uintptr(prot), uintptr(flags), fd, uintptr(offset)) 181 if err != 0 { 182 return 0, errno(err) 183 } 184 return result, nil 185 } 186 187 // mlock wraps the system call for mlock. 188 func mlock(addr, length uintptr) error { 189 _, _, err := syscall.Syscall(syscall.SYS_MLOCK, addr, length, 0) 190 if err != 0 { 191 return errno(err) 192 } 193 return err 194 } 195 196 // munlock wraps the system call for munlock. 197 func munlock(addr, length uintptr) error { 198 _, _, err := syscall.Syscall(syscall.SYS_MUNLOCK, addr, length, 0) 199 if err != 0 { 200 return errno(err) 201 } 202 return nil 203 } 204 205 // msync wraps the system call for msync. 206 func msync(addr, length uintptr) error { 207 _, _, err := syscall.Syscall(syscall.SYS_MSYNC, addr, length, syscall.MS_SYNC) 208 if err != 0 { 209 return errno(err) 210 } 211 return nil 212 } 213 214 // munmap wraps the system call for munmap. 215 func munmap(addr, length uintptr) error { 216 _, _, err := syscall.Syscall(syscall.SYS_MUNMAP, addr, length, 0) 217 if err != 0 { 218 return errno(err) 219 } 220 return nil 221 }