我对如何定义mmap调用实际返回的地址感到有点困惑,因为我看到了一段代码,其中这个地址被转换为uint64_t
并用作物理地址。
如果它是一个虚拟地址,并且我们需要一个物理地址,可以使用一个公式找到它,其中在打开proc / self / map后涉及带有pagesize等的模数。只是抽象它。
无论是否有大页面,我们如何对待此地址都很重要?
此外,如果哪种地址适合被归类为DMA-ABLE地址。通过代码示例,在内核中我们使用pci_alloc_consistent
和pci_map_single
来查找dma地址。假设在用户空间应用程序中,我希望dma TO / FROM设备并通过malloc或mmap为其tx和rx环分配一部分内存,我想要与此地址关联的物理地址。我应该只使用类型转换uint64_t(addr)
还是编写一个函数将此虚拟地址转换为等效于pci alloc consistent返回的dma句柄的物理地址。
从开源DPDK代码添加样本
mcfg = rte_eal_get_configuration()->mem_config;
/* hugetlbfs can be disabled */
if (internal_config.no_hugetlbfs) {
addr = mmap(NULL, internal_config.memory, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
if (addr == MAP_FAILED) {
RTE_LOG(ERR, EAL, "%s: mmap() failed: %s\n", __func__,
strerror(errno));
return -1;
}
mcfg->memseg[0].phys_addr = (phys_addr_t)(uintptr_t)addr; -----???
mcfg->memseg[0].addr = addr;
mcfg->memseg[0].len = internal_config.memory;
mcfg->memseg[0].socket_id = SOCKET_ID_ANY;
return 0;
}
在另一种情况下,它会通过此方式将从大页面映射的地址转换为物理地址。
/*
* Get physical address of any mapped virtual address in the current process.
*/
phys_addr_t
rte_mem_virt2phy(const void *virtaddr)
{
int fd;
uint64_t page, physaddr;
unsigned long virt_pfn;
int page_size;
off_t offset;
/* standard page size */
page_size = getpagesize();
fd = open("/proc/self/pagemap", O_RDONLY);
if (fd < 0) {
RTE_LOG(ERR, EAL, "%s(): cannot open /proc/self/pagemap: %s\n",
__func__, strerror(errno));
return RTE_BAD_PHYS_ADDR;
}
virt_pfn = (unsigned long)virtaddr / page_size;
offset = sizeof(uint64_t) * virt_pfn;
if (lseek(fd, offset, SEEK_SET) == (off_t) -1) {
RTE_LOG(ERR, EAL, "%s(): seek error in /proc/self/pagemap: %s\n",
__func__, strerror(errno));
close(fd);
return RTE_BAD_PHYS_ADDR;
}
if (read(fd, &page, sizeof(uint64_t)) < 0) {
RTE_LOG(ERR, EAL, "%s(): cannot read /proc/self/pagemap: %s\n",
__func__, strerror(errno));
close(fd);
return RTE_BAD_PHYS_ADDR;
}
/*
* the pfn (page frame number) are bits 0-54 (see
* pagemap.txt in linux Documentation)
*/
physaddr = ((page & 0x7fffffffffffffULL) * page_size)
+ ((unsigned long)virtaddr % page_size);
close(fd);
return physaddr;
}
答案 0 :(得分:1)
mmap()
返回指向新映射内存的指针。该指针指向程序看到的地址空间,可以像void*
类型的任何其他指针一样使用。如果失败,mmap()
会返回MAP_FAILED
,这是一个通常值为(void*)-1
的常量。
答案 1 :(得分:1)
正如其他人已经回答的那样,mmap()会返回一个虚拟地址。如果您需要物理地址(例如用户空间dma驱动程序),则可以转换 通过读取/ proc / self / pagemap和执行页面框架来虚拟到物理使用 数字数学,例如引用的DPDK例子。您需要注意几个警告:
答案 2 :(得分:0)
mmap()返回由remap_pfn_range为特定物理地址和长度创建的虚拟地址。 您可以使用virt_to_phys()获取物理地址。当然,这与像页面大小等模数相同,但完全依赖于体系结构,因为MMU在体系结构中是主观的。
总线地址用于将数据DMA到硬件。 pci_alloc_consistent等函数返回两个地址。 1.虚拟地址 2.公交车地址。
您的驱动程序可以使用虚拟内存寻址一大块RAM。因为MMU在CPU执行该指令时转换virt_to_phys。但在DMA操作期间,CPU被旁路,设备无法使用虚拟地址进行寻址。该设备插在连接到总线的接口(如PCI插槽)上(如PCI总线)。因此它只能使用总线地址来寻址。 使用总线地址的事实条件是不应该换出相应的存储器页面。 pci_alloc_consistent就是这么做的。 只要我们有一致的映射,我们就可以毫无问题地进行DMA。
uint64_t(addr)不会从虚拟地址中获取物理地址。而是使用virt_to_phys()。在这种情况下,当你可以使用virt和bus地址解决时,我不明白为什么你需要一个物理地址。如果需要,请使用它。