簡體   English   中英

mmap實際上返回什么?

[英]What does mmap actually return?

對於如何定義mmap調用實際返回的地址,我有些困惑,可能是因為我看到一段代碼,將該地址轉換為uint64_t並用作物理地址。 如果它是一個虛擬地址,並且我們需要一個物理地址,可以使用以下公式找到它:打開proc / self / map后,使用涉及pagesize等的模的公式。 只是抽象而已。

無論有無大量頁面,我們如何對待該地址是否重要?

同樣,哪種地址適合分類為DMA-ABLE地址。 在代碼示例中,我們在內核中使用pci_alloc_consistentpci_map_single查找dma地址。 假設在用戶空間應用程序中,我希望對TO / FROM設備進行dma處理,並通過malloc或mmap為其tx和rx環分配一部分內存,並且我想要與該地址關聯的物理地址。 我應該使用類型轉換uint64_t(addr)還是編寫一個函數將此虛擬地址轉換為等效於pci alloc返回的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;
}

mmap()將指針返回到新映射的內存中。 該指針指向程序可見的地址空間,並且可以像void*類型的任何其他指針一樣使用。 失敗時, mmap()返回MAP_FAILED ,這是一個常量,通常具有(void*)-1

正如其他人回答的那樣,mmap()返回一個虛擬地址。 如果您需要物理地址(例如,用於用戶空間dma驅動程序),則可以通過讀取/ proc / self / pagemap並執行頁面框架編號數學運算(例如,引用的DPDK示例進行此運算),將虛擬轉換為物理。 您需要注意以下幾點警告:

  • 根據內核版本,頁面幀號位可能有效或無效(有關詳細信息,請參閱Documentation / vm / pagemap.txt)
  • 僅mmap操作可能不足以使內核保留物理內存。 (通過read | write | lock)訪問內存應引起頁面錯誤,這將提示內核保留物理內存。
  • 如果您需要一個以上的頁面大小的內存,則可能需要做更多的工作-例如使用大頁面或實現類似於DPDK的操作,以按物理地址對內存進行排序,然后重新映射虛擬地址,以便物理內存和虛擬內存都是連續的。
  • 為了使虛擬到物理映射保持有效,內核不得將頁面換出內存。 DPDK通過mlock()處理每個頁面來處理此問題。

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()。 我不明白為什么在這種情況下可以使用虛擬地址和總線地址尋址時為什么需要一個物理地址。 如果仍然需要,請使用它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM