Memory management linux平台下的mmap查询

Memory management linux平台下的mmap查询,memory-management,linux-kernel,linux-device-driver,mmap,Memory Management,Linux Kernel,Linux Device Driver,Mmap,在Linux机器上,尝试编写驱动程序,并尝试将一些内核内存映射到应用程序,以提高性能。 在线检查mmap的驱动程序实现,查找不同的实现类型。 根据手册页,mmap-在调用进程的虚拟地址空间中创建新的映射 1) mmap呼叫期间谁分配物理地址空间?内核还是设备驱动程序 可以看到以下各种驱动程序mmap实现 a) 驱动程序创建连续的物理内核内存,并将其映射到进程地址空间 static int driver_mmap(struct file *filp, struct vm_area_struct *

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

1) mmap呼叫期间谁分配物理地址空间?内核还是设备驱动程序

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

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

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;
    }
}
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);
}
b) 驱动程序创建虚拟内核内存并将其映射到进程地址空间

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;
    }
}
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);
}
2) 若内核为mmap分配内存,在a&b情况下内存不是浪费了吗

3) remap_pfn_范围是映射多个页面,其中as vm_insert_页面仅用于单页面映射。这是这两个API的唯一区别吗

谢谢,


Gopinath.

你使用哪一个取决于你想要完成什么

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

使用(a),将分配一个物理上连续的空间。如果有某个外部硬件(例如PCI设备)将读取或写入该内存,则可以执行此操作。来自
kmalloc
的返回值已经具有到内核虚拟地址空间的映射
remap_pfn_range
也用于将页面映射到当前进程的用户虚拟地址空间

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

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;
    }
}
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),映射的内存是在其他子系统的控制下分配的。
vm_pgoff
字段已设置为资源的基本物理地址。例如,内存可能对应于PCI设备的地址区域(例如,网络接口控制器的寄存器),其中物理地址由BIOS(或机器使用的任何机制)确定/分配

(2) 我不太明白这个问题。如果设备驱动程序和协作用户进程正在使用内存,那么内存怎么会被“浪费”呢?如果内核需要读写内存,那么必须分配内核虚拟地址空间,并将其映射到底层物理内存。同样,如果用户空间进程要访问内存,则必须分配用户虚拟地址空间,并且必须将其映射到物理内存

“分配虚拟地址空间”本质上只是指为内存分配页表条目。这与实际分配物理内存是分开的。内核空间和用户空间是分开进行的。“映射”意味着将页表条目(页开头的虚拟地址)设置为指向正确的物理页地址


(3) 对。它们是不同的API,实现了几乎相同的功能。有时您有一个
struct页面
,有时您有一个
pfn
。这可能会让人困惑:完成同一件事通常有几种方法。开发人员通常对他们已经拥有的项目使用最明显的一个(“我已经有了一个
struct页面
。我可以计算它的
pfn
。但是,当有另一个API接受
struct页面
?)时,为什么要这样做呢?”

Thanx hamilton,请详细回答。