在 Go 中调用 mremap 不起作用,但不报错
Calling mremap in Go doesn't work, but gives no error
我正在尝试从 Go 中 mremap
一个文件,但文件的大小似乎没有改变,尽管返回了 0
的 errno
。当我尝试访问映射内存时,这会导致段错误。
我已经包含了下面的代码。该实现类似于sys
包中的mmap
实现,所以我不确定这里出了什么问题:
package main
import (
"fmt"
"io/ioutil"
"log"
"os"
"reflect"
"unsafe"
"golang.org/x/sys/unix"
)
// taken from <https://github.com/torvalds/linux/blob/f8394f232b1eab649ce2df5c5f15b0e528c92091/include/uapi/linux/mman.h#L8>
const (
MREMAP_MAYMOVE = 0x1
// MREMAP_FIXED = 0x2
// MREMAP_DONTUNMAP = 0x4
)
func mremap(data []byte, size int) ([]byte, error) {
header := (*reflect.SliceHeader)(unsafe.Pointer(&data))
mmapAddr, mmapSize, errno := unix.Syscall6(
unix.SYS_MREMAP,
header.Data,
uintptr(header.Len),
uintptr(size),
uintptr(MREMAP_MAYMOVE),
0,
0,
)
if errno != 0 {
return nil, fmt.Errorf("mremap failed with errno: %s", errno)
}
if mmapSize != uintptr(size) {
return nil, fmt.Errorf("mremap size mismatch: requested: %d got: %d", size, mmapSize)
}
header.Data = mmapAddr
header.Cap = size
header.Len = size
return data, nil
}
func main() {
log.SetFlags(log.LstdFlags | log.Lshortfile)
const mmPath = "/tmp/mm_test"
// create a file for mmap with 1 byte of data.
// this should take up 1 block on disk (4096 bytes).
err := ioutil.WriteFile(mmPath, []byte{0x1}, 0755)
if err != nil {
log.Fatal(err)
}
// open and stat the file.
file, err := os.OpenFile(mmPath, os.O_RDWR, 0)
if err != nil {
log.Fatal(err)
}
defer file.Close()
stat, err := file.Stat()
if err != nil {
log.Fatal(err)
}
// mmap the file and print the contents.
// this should print only one byte of data.
data, err := unix.Mmap(int(file.Fd()), 0, int(stat.Size()), unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED)
if err != nil {
log.Fatal(err)
}
fmt.Printf("mmap data: %+v\n", data)
// mremap the file to a size of 2 blocks.
data, err = mremap(data, 2*4096)
if err != nil {
log.Fatal(err)
}
// access the mremapped data.
fmt.Println(data[:4096]) // accessing the first block works.
fmt.Println(data[:4097]) // accessing the second block fails with `SIGBUS: unexpected fault address`.
}
我尝试寻找其他使用 mremap
的 Go 代码,但我似乎找不到。如果有任何意见,我将不胜感激!
正如@kostix 在评论中提到的,mmap
用于将常规文件映射到内存中。访问缓冲区导致段错误的原因是底层文件本身不够大。解决方案是在调用 mremap
:
之前将文件截断到所需的长度
if err := file.Truncate(2*4096); err != nil {
log.Fatal(err)
}
data, err = mremap(data, 2*4096)
我正在尝试从 Go 中 mremap
一个文件,但文件的大小似乎没有改变,尽管返回了 0
的 errno
。当我尝试访问映射内存时,这会导致段错误。
我已经包含了下面的代码。该实现类似于sys
包中的mmap
实现,所以我不确定这里出了什么问题:
package main
import (
"fmt"
"io/ioutil"
"log"
"os"
"reflect"
"unsafe"
"golang.org/x/sys/unix"
)
// taken from <https://github.com/torvalds/linux/blob/f8394f232b1eab649ce2df5c5f15b0e528c92091/include/uapi/linux/mman.h#L8>
const (
MREMAP_MAYMOVE = 0x1
// MREMAP_FIXED = 0x2
// MREMAP_DONTUNMAP = 0x4
)
func mremap(data []byte, size int) ([]byte, error) {
header := (*reflect.SliceHeader)(unsafe.Pointer(&data))
mmapAddr, mmapSize, errno := unix.Syscall6(
unix.SYS_MREMAP,
header.Data,
uintptr(header.Len),
uintptr(size),
uintptr(MREMAP_MAYMOVE),
0,
0,
)
if errno != 0 {
return nil, fmt.Errorf("mremap failed with errno: %s", errno)
}
if mmapSize != uintptr(size) {
return nil, fmt.Errorf("mremap size mismatch: requested: %d got: %d", size, mmapSize)
}
header.Data = mmapAddr
header.Cap = size
header.Len = size
return data, nil
}
func main() {
log.SetFlags(log.LstdFlags | log.Lshortfile)
const mmPath = "/tmp/mm_test"
// create a file for mmap with 1 byte of data.
// this should take up 1 block on disk (4096 bytes).
err := ioutil.WriteFile(mmPath, []byte{0x1}, 0755)
if err != nil {
log.Fatal(err)
}
// open and stat the file.
file, err := os.OpenFile(mmPath, os.O_RDWR, 0)
if err != nil {
log.Fatal(err)
}
defer file.Close()
stat, err := file.Stat()
if err != nil {
log.Fatal(err)
}
// mmap the file and print the contents.
// this should print only one byte of data.
data, err := unix.Mmap(int(file.Fd()), 0, int(stat.Size()), unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED)
if err != nil {
log.Fatal(err)
}
fmt.Printf("mmap data: %+v\n", data)
// mremap the file to a size of 2 blocks.
data, err = mremap(data, 2*4096)
if err != nil {
log.Fatal(err)
}
// access the mremapped data.
fmt.Println(data[:4096]) // accessing the first block works.
fmt.Println(data[:4097]) // accessing the second block fails with `SIGBUS: unexpected fault address`.
}
我尝试寻找其他使用 mremap
的 Go 代码,但我似乎找不到。如果有任何意见,我将不胜感激!
正如@kostix 在评论中提到的,mmap
用于将常规文件映射到内存中。访问缓冲区导致段错误的原因是底层文件本身不够大。解决方案是在调用 mremap
:
if err := file.Truncate(2*4096); err != nil {
log.Fatal(err)
}
data, err = mremap(data, 2*4096)