zoukankan      html  css  js  c++  java
  • Linux内核驱动--mmap设备方法【原创】

    mmap系统调用(功能)

    void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset)

    内存映射函数mmap , 负责把文件内容映射到进程的虚拟内存空间,通过对这段内存的读取和修改,来实现对文件的读取和修改,而不需要再调用read, write等操作。

     

    addr:        指定映射的起始地址,通常设为NULL, 由系统指定。

    len:          映射到内存的文件长度

    prot:      映射区的保护方式, 可以是:

    PROT_EXEC:  映射区可被执行

    PROT_READ:  映射区可被读取

    PROT_WRITE: 映射区可被写入

    flags:        映射区的特性,可以是:

                     MAP_SHARED:

                     写入映射区的数据会复制回文件,且允许其他映射该文件的进程共享

                     MAP_PRIVATE:

    对映射区的写入操作会产生一个映射区的复制(copy-on-write), 对此区域所做的修改不会写回原文件。

    fd:            由open返回的文件描述符,代表要映射的文件

    offset:      以文件开始处的偏移量,必须是分页大小的整数倍,通常为0,表示从文件开头映射

    解除映射

    int munmap(void *start, size_t length)

    功能:

                     取消参数start所指向的映射内存,参数length表示欲取消的内存大小

    返回值:

                     解除成功返回0,否则返回-1,错误原因存于errno中。

    注意:

    mmap      不影响原文件的长度,如果写入的长度超出了原文件的长度,那么就只能写入原文件的长度大小的数据内容

    代码:

    #include <stdio.h>

     #include <string.h>                                                                                

     #include <sys/types.h>

     #include <sys/stat.h>

     #include <fcntl.h>

     #include <unistd.h>

     #include <sys/mman.h>

     int main(void)

    -{

    |    int fd;

    |    char *start=NULL;

    |    char buf[100];

    |

    |    fd = open("t.txt", O_RDWR | O_CREAT);

    |    start = mmap(NULL, 100, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

    |       

    |    strcpy(buf, start);

    |    printf("buf is %s ", buf);

    |

    |    strcpy(start, "sky is hello");

    |    munmap(start, 100);

    |    close(fd);

    |

    |    return 0;

    |}

    虚拟内存区域

         虚拟内存区域是进程的虚拟地址空间中的一个同质区间,即具有同样特性的连续地址

    范围。一个进程的内存映像由下面几部分组成:程序代码、数据、BSS和栈区域,以及内存

    映射的区域。

    一个进程的内存区域可以通过 /proc/pid/maps来查看

    例如:cat   /proc/6/maps 

    每一行的域为:

    Start_end perm offset major:minor inode

    Start: 该区域起始虚拟地址

    End:该区域结束虚拟地址

    Perm:读、写和执行权限;表示对这个区域,允许进程做什么。这个域的最后一个字符要么是p表示私有的,要么是s表示共享的

    Offset: 被映射部分在文件中的起始地址

    Major、minor: 主次设备号

    Inode: 索引结点

    Linux内核使用结构vm_area_struct

    (<linux/mm_types.h>)来描述虚拟内存区域,其中几个主要成员如下:

    unsigned long vm_start   虚拟内存区域起始地址

    unsigned long vm_end    虚拟内存区域结束地址

    unsigned long vm_flags    该区域的标记。如:VM_IO和VM_RESERVED。

    VM_IO将该VMA标记为内存映射的IO区域,VM_IO会阻止系统将该区域包含在进程的存放转存(core dump)中, VM_RESERVED 标志内存区域不能被换出。

    mmap设备操作

          映射一个设备是指把用户空间的一段地址关联到设备内存上。当程序读写这段用户空间的地址时,它实际上是在访问设备。

    内存管理单元去关联的,通过页式管理关联的,具体如何关联的去了解linux内存管理吧。

    mmap设备方法需要完成什么功能?

    Mmap方法是file_oprations结构的成员,在mmap系统调用发出时被调用。在此之前,内核已经完成了很多工作。Mmap设备方法所需要做的就是建立虚拟地址到物理地址的页表。

    int  (*mmap) (struct file*, struct vm_area_struct *)

    mmap如何完成页表的建立?

    方法有二:

    1、  使用remap_pfn_range一次建立所有页表;

    2、  使用nopage  VMA方法每次建立一个页表

    构造页表的工作可由remap_pfn_range函数完成,原型如下:

    int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn, unsigned long size, pgprot_t prot)

    vma:                         虚拟内存区域指针

    virt_addr:        虚拟地址的起始值

    pfn:                          要映射的物理地址所在的物理页帧号,可将物理地址>>PAGE_SHIFT得到,这个宏定义是12,就是除以4KB

    size:              要映射的区域的大小

    prot:              VMA的保护属性

    例子:

    int memdev_mmap(struct file *filp, struct vm_area_struct *vma)

    {

            //设置保护属性

            vma->vm_flags |= VM_IO;

            vma->vm_flags |= VM_RESERVED;

            if (remap_pfm_range(vma, vma->vma-start, virt_to_phys(dev->data)>>PAGE_SHIFT,

    size, vma->vm_page_prot))

                     return –EAGAIN;

            return 0;

    }

    Dev-data是虚拟地址,就要用virt_to_phys转换为物理地址,如果是物理地址,那就不用转化了。

    这样就实现了mmap的设备操作

    欢迎交流
    如有转载请注明出处

    新浪博客:http://blog.sina.com.cn/u/2049150530
    博客园:http://www.cnblogs.com/sky-heaven/
    知乎:http://www.zhihu.com/people/zhang-bing-hua

  • 相关阅读:
    【原创】【JNI】OPUS压缩与解压的JNI调用(.DLL版本)
    线性基学习笔记
    杜教筛&Min_25筛学习笔记
    LOJ2540 随机算法
    仙人掌&圆方树学习笔记
    CF487E Tourists
    BZOJ2125 最短路
    [SHOI2008]仙人掌图
    BZOJ4316 小C的独立集
    NOI2015 品酒大会
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/4508147.html
Copyright © 2011-2022 走看看