Go mmap 文件内存映射

Go mmap 文件内存映射

mmap是个很好用的内存映射工具,它可以将文件映射到内存中,可以方便地操作文件。使用mmap的优点是:

  • 内存映射可以使得读写文件的性能更高,因为操作的是内存而不是磁盘。
  • 可以方便地操作文件,不需要再通过文件操作的方式打开读写文件。
  • 方便多线程操作文件。

Go mmap 文件内存映射_第1张图片

优点

mmap 另一个非常重要的特性是:减少内存的拷贝次数。在 linux 系统中,文件的读写操作通常通过 read 和 write 这两个系统调用来实现,这个过程会产生频繁的内存拷贝。比如 read 函数就涉及了 2 次内存拷贝:

  • 操作系统读取磁盘文件到页缓存
  • 从页缓存将数据拷贝到 read 传递的 buf 中
    mmap 只需要一次拷贝。即操作系统读取磁盘文件到页缓存,进程内部直接通过指针方式修改映射的内存。因此 mmap 特别适合读写频繁的场景,既减少了内存拷贝次数,提高效率,又简化了操作。KV数据库 bbolt 就使用了这个方法持久化数据。

例子

package main
 
import (
    "fmt"
    "log"
    "os"
    "syscall"
    "unsafe"
)
 
const defaultMaxFileSize = 1 << 30        // 假设文件最大为 1G
const defaultMemMapSize = 128 * (1 << 20) // 假设映射的内存大小为 128M
 
func main() {
    mmpFile := NewMmpFile("test.txt")
    defer mmpFile.munmap()
    defer mmpFile.file.Close()
    msg := "hello csdn colinrs!"
 
    mmpFile.grow(int64(len(msg) * 2))
    for i, v := range msg {
        mmpFile.data[i] = byte(v)
    }
}
 
type MmpFile struct {
    file    *os.File
    data    *[defaultMaxFileSize]byte
    dataRef []byte
}
 
func NewMmpFile(fileName string) *MmpFile {
    file, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE, 0666)
    if err != nil {
        log.Fatalf("Open file error: %v", err)
    }
    mmpFile := &MmpFile{file: file}
    mmpFile.mmap()
    return mmpFile
}
 
func _assert(condition bool, msg string, v ...interface{}) {
    if !condition {
        panic(fmt.Sprintf(msg, v...))
    }
}
 
func (mmpFile *MmpFile) mmap() {
    b, err := syscall.Mmap(int(mmpFile.file.Fd()), 0,
        defaultMemMapSize, syscall.PROT_WRITE|syscall.PROT_READ, syscall.MAP_SHARED)
    _assert(err == nil, "failed to mmap", err)
    mmpFile.dataRef = b
    mmpFile.data = (*[defaultMaxFileSize]byte)(unsafe.Pointer(&b[0]))
}
 
func (mmpFile *MmpFile) grow(size int64) {
    if info, _ := mmpFile.file.Stat(); info.Size() >= size {
        return
    }
    _assert(mmpFile.file.Truncate(size) == nil, "failed to truncate")
}
 
func (mmpFile *MmpFile) munmap() {
    _assert(syscall.Munmap(mmpFile.dataRef) == nil, "failed to munmap")
    mmpFile.data = nil
    mmpFile.dataRef = nil
}
  • 遇到 mmp 文件报错为 syscall.Errno=permission denied),
    • 可以参考:syscall.Errno=permission denied)
    • 将读取文件修改为 os.OpenFile(fileName, os.O_RDWR|os.O_CREATE, 0666)

参考

  • Go Mmap 文件内存映射简明教程
  • 阅读笔记:零拷贝及一些引申内容

你可能感兴趣的:(Golang,golang,数据库,开发语言)