使用sendfile()/fcopyfile()从共享内存映射对象复制数据

10

是否可以并且是否明智使用sendfile()(或其Darwin/BSD衍生版本fcopyfile())在共享内存对象和文件之间直接传输数据?

sendfile()fcopyfile()等函数可以完全在内核空间中执行所有机械必需品,从而实现这种数据传输-当调用这些函数时,您将传递两个打开描述符,一个源和一个目标,它们会从那里接管。

其他复制数据的方法无论如何都需要手动穿越内核空间和用户空间之间的边界。这种上下文切换从性能上来说本质上相当昂贵。

我找不到关于像这样使用共享内存描述符作为参数的确切信息:没有支持或反对这种做法的文章;在各自的man页面中也没有发现任何相关内容;没有公开考虑将sendfile()用于共享内存描述符的Twitter推文以及其他……但是,我认为我应该能够做到这一点:

char const* name = "/yo-dogg-i-heard-you-like-shm"; /// only one slash, at zero-index
int len = A_REASONABLE_POWER_OF_TWO;                /// valid per shm_open()
int descriptor = shm_open(name, O_RDWR | O_CREAT, 0600);
int destination = open("/tmp/yodogg.block", O_RDWR | O_CREAT, 0644);
void* memory = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, descriptor, 0);
off_t bytescopied = 0;
sendfile(destination, descriptor, &bytescopied, len);
/// --> insert other stuff with memset(…), memcopy(…) &c. here, possibly
munmap(memory, len);
close(descriptor); close(destination);
shm_unlink(name);

这是错误的方法还是有效的技术?

如果是后者,是否可以在复制数据之前调整内存共享映射的大小?


编辑:我正在 macOS 10.12.4 上开发与此查询相关的项目;我旨在使其在 Linux 上工作,并最终实现 FreeBSD 的互操作性。


你尝试过它,发生了什么事? - user207421
@EJP,我还没有尝试过这个确切的方案——shm_open()mmap(…, MAP_SHARED, …)sendfile()的三连击——有两个原因。首先,一个小问题:我现在正在macOS上本地开发,所以我的sendfile()是使用fcopyfile()的polyfill...第二:我的当前任务是通过其包装类实现对可写文件映射内存的访问(该包装器已经提供只读内存映射,请参见https://github.com/fish2000/libimread/blob/master/src/file.cpp#L115-L140);我将利用我在这方面学到的知识以及从SO上获得的知识来解决共享内存问题。 - fish2000
1个回答

4

在两个内存映射的“thing”之间复制数据(如上面的示例)确实需要从内核复制到用户空间,然后再次复制回去。很遗憾,你真的不能使用sendfile(2)系统调用发送到文件描述符。

但你可以像这样做:

  1. 创建共享内存对象(或文件;由于第二步,它将在内存中共享)
  2. 使用MAP_SHARED将其映射到内存中,您将获得一个指针
  3. 打开目标文件
  4. write(destination_fd,source_pointer,source_length)

在这种情况下,write系统调用不需要将数据复制到您的进程中。不确定实际的性能特征是什么。巧妙地使用madvise(2)可能会有所帮助。


很好知道 - 感谢您关于使用 write(...) 的提示。乍一看,根据平台的情况,明智地使用 madvise(...)fadvise(...)(使用 FADV_SEQUENTIAL)可能是最佳选择。 - fish2000

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接