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

Copying data from a shared-memory-mapped object using sendfile()/fcopyfile()

是否有可能——如果谨慎的话——使用sendfile()(或其Darwin/BSD堂兄弟fcopyfile())直接在共享内存对象和文件之间传送数据?

sendfile()fcopyfile()这样的函数可以完全执行支持这种数据传输的所有机械必需品,而无需离开内核-space——你传递两个开放的描述符,一个源和目的地,当调用这些函数时,他们从那里获取它。

其他复制数据的方法总是需要手动操作内核-space 和用户-space 之间的边界;这种上下文切换本质上是相当昂贵的,性能方面。

我找不到关于使用共享内存描述符作为论据的任何明确内容:没有支持或反对这种做法的文章;相应的 man 页中没有任何内容;没有公开认为 sendfile()-ing 共享内存描述符有害的推文; &c……但是,我想我应该可以做这样的事情:

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 互操作。

在内存中映射的两个 "things" 之间复制数据 - 就像上面的例子一样 - 确实需要将内容从内核复制到用户空间,然后再复制回来。不,恐怕你不能真正使用 sendfile(2) 系统调用发送到文件描述符。

但你应该可以这样做:

  1. 创建共享内存对象(或者一个文件,真的;由于第二步,它无论如何都会在内存中共享
  2. 将其映射到内存中,MAP_SHARED;你会得到一个指针
  3. 打开目标文件
  4. 写入(destination_fd, source_pointer, source_length)

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