将内存映射缓冲区到文件句柄。

huangapple go评论78阅读模式
英文:

mmap buffer to file handle

问题

我需要“伪造”一个支持fstat的文件描述符,我是这样做的。

func ScanBytes(b []byte) error {
  size := C.size_t(len(b))
  path := C.CString("/bytes")
  fd := C.shm_open(path, C.O_RDWR|C.O_CREAT, C.mode_t(0600))
  defer C.shm_unlink(path)
  defer C.close(fd)

  res := C.ftruncate(fd, C.__off_t(size))
  if res != 0 {
    return fmt.Errorf("无法分配共享内存区域(%d)", res)
  }

  var addr = unsafe.Pointer(&b[0])
  C.mmap(addr, size, C.PROT_READ|C.PROT_WRITE, C.MAP_SHARED|C.MAP_FIXED, fd, 0)
  defer C.munmap(addr, size)

  // _, err := syscall.Write(int(fd), b)

  return doSomethingWith(fd)
}

你可以看到文件句柄的写操作被注释掉了。

如果我不将缓冲区写入分配的区域,它将为空。
我原本希望mmapMAP_FIXED一起使用时,会使用提供的缓冲区的地址,从而将内容映射到该区域。

我猜测write调用会复制数据,从而使内存使用量加倍。我真的需要执行write操作吗?

英文:

I need to "fake" a file descriptor (that supports fstat) and I do it like this.

func ScanBytes(b []byte) error {
  size := C.size_t(len(b))
  path := C.CString("/bytes")
  fd := C.shm_open(path, C.O_RDWR|C.O_CREAT, C.mode_t(0600))
  defer C.shm_unlink(path)
  defer C.close(fd)

  res := C.ftruncate(fd, C.__off_t(size))
  if res != 0 {
    return fmt.Errorf("could not allocate shared memory region (%d)", res)
  }

  var addr = unsafe.Pointer(&b[0])
  C.mmap(addr, size, C.PROT_READ|C.PROT_WRITE, C.MAP_SHARED|C.MAP_FIXED, fd, 0)
  defer C.munmap(addr, size)

  // _, err := syscall.Write(int(fd), b)

  return doSomethingWith(fd)
}

You see where the write to the file handle is commented out.

If I don't write the buffer to the allocated region, it is empty.
I was hoping mmap in conjunction with MAP_FIXED would use the address of the provided buffer, thus mapping the contents to that region.

I guess the write call copies, thus doubling the memory usage. Do I really have to write?

答案1

得分: 2

听起来你想要通过文件描述符访问一个现有的内存区域,而不进行复制。

这个问题听起来非常像这个关于mmap的之前的stackoverflow问题

对于那个答案来说,没有太多可以补充的。如果你不能控制b的分配,那么在没有复制的情况下是不可能做到这一点的。接受需要进行复制的事实后,你的shm解决方案是不错的。不需要使用mmap。

从mmap的man页面中,关于MAP_FIXED的定义:

>不允许系统选择一个与指定地址不同的地址。如果指定的地址不能使用,mmap()将失败。
>如果指定了MAP_FIXED,addr必须是页面大小的倍数。如果MAP_FIXED请求成功,mmap()建立的映射将替换进程在addr到addr + len范围内的任何先前映射。不鼓励使用此选项。

很可能unsafe.Pointer(&b[0])不是页面大小的倍数,特别是如果底层数组很小的话,mmap会失败。一如既往,检查返回值

如果mmap成功,映射将替换任何先前的映射,这意味着你的数据现在已经消失。读取切片将会得到零填充的字节。

英文:

It sounds like what you want is to be able to access an existing memory region via a file descriptor without copying.

The problem sounds very much like this previous stackoverflow question about mmap.

There isn't much to add to that answer there. It isn't possible to do this without a copy, given that you don't control the allocation of b. Accepting that a copy is required, your shm solution is good. mmap is not needed.

From the man page for mmap, the definition of MAP_FIXED:

>Do not permit the system to select a different address than the one specified. If the specified address cannot be
> used, mmap() will fail.
> If MAP_FIXED is specified, addr must be a multiple of the pagesize. If a MAP_FIXED request is successful, the
> mapping established by
> mmap() replaces any previous mappings for the process' pages in the range from addr to addr + len. Use of this
> option is discouraged.

It is quite possible that unsafe.Pointer(&b[0]) is not a multiple of the page size, especially if the underlying array is small. mmap would fail in that case. As always, check the return value.

If mmap does succeed, the mapping replaces any previous mapping, meaning your data is now gone. Reading the slice would give you zeroed bytes.

huangapple
  • 本文由 发表于 2013年11月22日 00:24:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/20126447.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定