go mmap使用

相关概念

Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error)
- prot:内存保护标志位,可以通过或运算符`|`组合
    - PROT_EXEC  // 页内容可以被执行
    - PROT_READ  // 页内容可以被读取
    - PROT_WRITE // 页可以被写入
    - PROT_NONE  // 页不可访问
- flags:映射对象的类型,常用的是以下两类
    - MAP_SHARED  // 共享映射,写入数据会复制回文件, 与映射该文件的其他进程共享。
    - MAP_PRIVATE // 建立一个写入时拷贝的私有映射,写入数据不影响原文件。

代码

package mmap

import (
    "fmt"
    "os"
    "syscall"
    "unsafe"
)

const maxMapSize int = 0xFFFFFFFFFFFF

type DB struct {
    file    *os.File
    dataref []byte
    data    *[maxMapSize]byte
    datasz  int
}

// mmap memory maps a DB's data file.
func mmap(db *DB, sz int) error {
    // Map the data file to memory.
    b, err := syscall.Mmap(int(db.file.Fd()), 0, sz, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
    if err != nil {
       return err
    }

    // Advise the kernel that the mmap is accessed randomly.
    if err := madvise(b, syscall.MADV_RANDOM); err != nil {
       return fmt.Errorf("madvise: %s", err)
    }

    // Save the original byte slice and convert to a byte array pointer.
    db.dataref = b
    db.data = (*[maxMapSize]byte)(unsafe.Pointer(&b[0]))
    db.datasz = sz
    return nil
}

// munmap unmaps a DB's data file from memory.
func munmap(db *DB) error {
    // Ignore the unmap if we have no mapped data.
    if db.dataref == nil {
       return nil
    }

    // Unmap using the original byte slice.
    err := syscall.Munmap(db.dataref)
    db.dataref = nil
    db.data = nil
    db.datasz = 0
    return err
}

// NOTE: This function is copied from stdlib because it is not available on darwin.
func madvise(b []byte, advice int) (err error) {
    _, _, e1 := syscall.Syscall(syscall.SYS_MADVISE, uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)), uintptr(advice))
    if e1 != 0 {
       err = e1
    }
    return
}

// 更改文件大小,如果不更改大小的话,可能导致mmap调用panic或者写数据失败
func (d *DB) grow(size int64) {
    if info, _ := d.file.Stat(); info.Size() >= size {
       return
    }
    if err := d.file.Truncate(size); err != nil {
       panic(err)
    }
}

测试用例

package mmap

import (
    "fmt"
    "os"
    "testing"
)

func TestMmap(t *testing.T) {
    f, err := os.OpenFile("./tmp.txt", os.O_CREATE|os.O_RDWR, 0644)
    if err != nil {
       panic(err)
    }
    db := &DB{file: f}
    err = mmap(db, 1<<10)
    if err != nil {
       panic(err)
    }
    msg := []byte("hello,world1234")
    sz := int64(len(msg))
    // 必须要进行这一步,否则存储失败或者报panic
    // 用于修改文件的大小,Linux/unix 不允许操作超过文件大小之外的内存地址
    db.grow(sz)
    for i := 0; i < len(msg); i++ {
       db.data[i] = msg[i]
    }
    fmt.Println(string(db.data[:sz]))
    munmap(db)
}

文件中结果如下

>>> cat tmp.txt 
>>> hello,world1234% 

你可能感兴趣的:(golang进阶之路,源码学习,golang,开发语言,后端,经验分享,笔记)