在用户空间中使用DMA内存传输

时间:2014-05-10 11:05:46

标签: linux dma

用户空间是否有可用的linux DMA mem-to-mem复制机制?

我有一个Linux应用程序,通常(每秒50-100次)必须记忆几个megs(10+)的数据。通常它不是问题,但我们已经开始看到证据表明它可能消耗了太多的CPU带宽。目前的测量结果表明我们正在以1Gbytes / s的速度移动。

我知道内核中的dma功能,我看到一些文档讨论为大内存副本构建自定义驱动程序,原因就在这个原因。但似乎有人会构建一个通用的API为此,现在。我错了吗? DMA是仅内核功能吗?

我应该澄清一下,这是针对英特尔X86架构,而不是嵌入式。

2 个答案:

答案 0 :(得分:8)

  • Linux的DMA API不允许内存到内存传输。它仅用于设备和内存之间的通信。查看Documentation/DMA-API.txt了解更多详情。

  • 在硬件级别,x86 DMA控制器不允许内存进行内存传输。这里已经讨论过:DMA transfer RAM-to-RAM

  • 鉴于内存总线通常比CPU慢,启动内核驱动的内存副本有什么好处?您仍然需要等待传输完成,其持续时间仍然取决于内存带宽,与CPU驱动的副本完全相同。

  • 如果程序的性能完全取决于内存到内存复制性能,则意味着可以通过尽可能避免复制或通过实现更智能的过程(如写入时复制)来大幅提高性能。 / p>

答案 1 :(得分:2)

听起来你真正想要的是写时复制语义。这意味着默认情况下根本不会创建任何副本,但是如果任何给定的线程需要更改部分数据,那么只有该页面的副本才会在使用时透明。

如果您的数据足够大,以致这些memcpy来电受到影响,写保留即可为您节省许多费用:

  • 不重复相同的数据(在页面级别) - 减少工作集的大小
  • 在实际需要之前不会浪费任何提取/存储操作

DMA不是解决方案,它主要用于设备主机或设备设备通信,而不是以可用的方式暴露给普通用户空间进程。

相反,您可以使用POSIX共享内存来获取此行为:

#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>

int main() {
  // Once:
  int fd = shm_open("/cowalloc", O_RDWR|O_CREAT, 0600);
  shm_unlink("/cowalloc");
  ftruncate(fd, 1024); // This is the size of the COW regiona
  char *master = mmap(NULL, 1024, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

  strcpy(master, "hello world, this is a demonstration of COW behaviour in Linux");

  // Per thread:
  char *thread = mmap(NULL, 1024, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_NORESERVE, fd, 0);

  // Demo
  printf("Master: %s\nThread: %s\n", master, thread);
  printf("\nChanging in thread:\n");
  strcpy(thread, "This is a private change");
  printf("Master: %s\nThread: %s\n", master, thread);

  return 0;
}

这里的基本思想是,一旦使用MAP_SHARED,您就可以完成数据的所有全局设置(可能是从磁盘/网络或计算中加载)。然后,您可以使用相同的文件描述符再次调用mmap,以便为您认为可能需要写入本地副本的每个线程进行其他私有映射。

此处使用MAP_NORESERVE标志是可选的 - 如果您只在每个线程中更改一个页面中的一个页面,那么使用它可能是有意义的,以避免不必要地抓取大量交换。

(请注意,如果您从磁盘加载,只需在文件上使用mmap即可进一步优化。

当然,在对象级别执行COW行为可能更干净,更便携,例如使用COW智能指针类型。