linux 平台上的 mmap 查询

mmap query on linux platform

在 Linux 机器上,尝试编写驱动程序并尝试将一些内核内存映射到应用程序以提高性能。 在线检查 mmap 的驱动程序实现,找到不同的实现方式。 根据手册页,mmap - 在调用进程的虚拟地址 space 中创建新映射。

1) mmap调用时谁分配物理地址space?内核或设备驱动程序?

看到了以下各种驱动程序 mmap 实现。

a) 驱动程序创建连续的物理内核内存并将其映射到进程地址space。

static int driver_mmap(struct file *filp, struct vm_area_struct *vma)
{
    unsigned long size = vma->vm_end - vma->vm_start;

    pos = kmalloc(size); //allocate contiguous physical memory.
    while (size > 0) {
        unsigned long pfn;
        pfn = virt_to_phys((void *) pos) >> PAGE_SHIFT; // Get Page frame number
        if (remap_pfn_range(vma, start, pfn, PAGE_SIZE, PAGE_SHARED)) // creates mapping
            return -EAGAIN;
        start += PAGE_SIZE;
        pos += PAGE_SIZE;
        size -= PAGE_SIZE;
    }
}

b) 驱动程序创建虚拟内核内存并将其映射到进程地址 space.

static struct vm_operations_struct dr_vm_ops = {
    .open = dr_vma_open,
    .close = dr_vma_close,
};
static int driver_mmap(struct file *filp, struct vm_area_struct *vma)
{
    unsigned long size = vma->vm_end - vma->vm_start;
    void *kp = vmalloc(size);
    unsigned long up;

    for (up = vma->vm_start; up < vma->vm_end; up += PAGE_SIZE) {
        struct page *page = vmalloc_to_page(kp); //Finding physical page from virtual address
        err = vm_insert_page(vma, up, page); //How is it different from remap_pfn_range?
        if (err)
            break;
        kp += PAGE_SIZE;
    }

    vma->vm_ops = &dr_vm_ops;
    ps_vma_open(vma);
}

c) 不确定在这种情况下谁分配内存。

static int driver_mmap(struct file *filp, struct vm_area_struct *vma)
{
    if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
                vma->vm_end - vma->vm_start,
                vma->vm_page_prot)) // creates mapping
        return -EAGAIN;
}

2) 如果kernel为mmap分配内存,那么a&b情况下是不是浪费了内存?

3) remap_pfn_range 用于映射多个页面,而 vm_insert_page 仅用于单页映射。这是这两个 API 的唯一区别吗b/w?

谢谢,

戈皮纳特。

使用哪种取决于您要实现的目标。

(1) 设备驱动程序是 内核的一部分 因此以这种方式进行区分并没有多大意义。对于这些情况,设备驱动程序要求从整个内核可用的(物理)内存资源中分配内存供自己使用。

对于 (a),正在分配物理上连续的 space。如果有一些外部硬件(例如 PCI 设备)将读取或写入该内存,您可能会这样做。来自 kmalloc 的 return 值已经映射到内核虚拟地址 space。 remap_pfn_range 也用于将页面映射到当前进程的用户虚拟地址 space。

对于 (b),正在分配 虚拟 连续 space。如果不涉及外部硬件,这就是您通常使用的。仍然有物理内存分配给您的驱动程序,但不能保证这些页面 物理上 是连续的——因此可以分配哪些页面的限制较少。 (它们在内核 virtual 地址 space 中仍然是连续的。)然后你只是使用不同的 API 来实现到用户虚拟的相同类型的映射地址 space.

对于 (c),被映射的内存是在其他一些子系统的控制下分配的。 vm_pgoff 字段已经设置为资源的基本物理地址。例如,内存可能对应于 PCI 设备的地址区域(例如网络接口控制器的寄存器),其中该物理地址由您的 BIOS(或您的机器使用的任何机制)determined/assigned。

(2) 不确定我是否理解这个问题。如果设备驱动程序和协作用户进程正在使用内存,内存怎么会是 "wasted" 呢?而如果内核需要读写内存,则必须分配内核虚拟地址space,并映射到底层物理内存。同样,如果用户 space 进程要访问内存,则必须分配用户虚拟地址 space 并且 that 也必须映射到物理内存.

"Allocating virtual address space" 本质上只是意味着为内存分配页面 table 条目。这是与实际分配物理内存分开完成的。它是针对内核 space 和用户 space 分别完成的。而"mapping"表示设置页table入口(页首的虚拟地址)指向正确的物理页地址。

(3) 是的。它们是不同的 API,但完成的事情几乎相同。有时你有 struct page,有时你有 pfn。这可能会造成混淆:通常有多种方法可以完成同一件事。开发人员通常使用最明显的项目来表示他们已有的项目 ("I already have a struct page. I could calculate its pfn. But why do that when there's this other API that accepts a struct page?")。