Mmap系列三: 驱动里mmap的实现

转载:驱动总结之mmap函数实现

设备驱动的mmap实现主要是将一个物理设备的可操作区域(设备空间)映射到一个进程的虚拟地址空间。这样就可以直接采用指针的方式像访问内存的方式访问设备。

在驱动中的mmap实现主要是完成一件事,就是实际物理设备的操作区域进程虚拟空间地址的映射过程。同时也需要保证这段映射的虚拟存储器区域不会被进程当做一般的空间使用,因此需要添加一系列的保护方式。

具体的实现过程如下:

/*主要是建立虚拟地址到物理地址的页表关系,其他的过程又内核自己完成*/

static int mem_mmap(struct file* filp,struct vm_area_struct *vma)

{

    /*间接的控制设备*/

    struct mem_dev *dev = filp->private_data;

    

    /*标记这段虚拟内存映射为IO区域,并阻止系统将该区域包含在进程的存放转存中*/

    vma->vm_flags |= VM_IO;

    /*标记这段区域不能被换出*/

    vma->vm_flags |= VM_RESERVED;



    /**/

    if(remap_pfn_range(vma,/*虚拟内存区域*/

        vma->vm_start, /*虚拟地址的起始地址*/

        virt_to_phys(dev->data)>>PAGE_SHIFT, /*物理存储区的物理页号*/

     dev->size,    /*映射区域大小*/        

        vma->vm_page_prot /*虚拟区域保护属性*/    

        ))

        return -EAGAIN;



    return 0;

}

具体的实现分析如下:

vma->vm_flags |= VM_IO;

vma->vm_flags |= VM_RESERVED;

上面的两个保护机制就说明了被映射的这段区域具有映射IO的相似性,同时保证这段区域不能随便的换出。就是建立一个物理页与虚拟页之间的关联性。具体原理是虚拟页和物理页之间是以页表的方式关联起来,虚拟内存通常大于物理内存,在使用过程中虚拟页通过页表关联一切对应的物理页,当物理页不够时,会选择性的牺牲一些页,也就是将物理页与虚拟页之间切断,重现关联其他的虚拟页,保证物理内存够用。在设备驱动中应该具体的虚拟页和物理页之间的关系应该是长期的,应该保护起来,不能随便被别的虚拟页所替换。具体也可参看关于虚拟存储器的文章。

接下来就是建立物理页与虚拟页之间的关系,即采用函数remap_pfn_range(),具体的参数如下:

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

1、struct vm_area_struct是一个虚拟内存区域结构体,表示虚拟存储器中的一个内存区域。其中的元素vm_start是指虚拟存储器中的起始地址。

2、addr也就是虚拟存储器中的起始地址,通常可以选择addr = vma->vm_start。

3、pfn是指物理存储器的具体页号,通常通过物理地址得到对应的物理页号,具体采用virt_to_phys(dev->data)>>PAGE_SHIFT.首先将虚拟内存转换到物理内存,然后得到页号。>>PAGE_SHIFT通常为12,这是因为每一页的大小刚好是4K,这样右移12相当于除以4096,得到页号。

4、size区域大小

5、区域保护机制。

返回值,如果成功返回0,否则正数。

测试代码可以直接通过对虚拟内存区域操作,实现不同的操作,如下:

  1. #include<fcntl.h>
    #include<unistd.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<sys/mman.h>
    #include<string.h>
    
    int main()
    {
        int fd;
        char *start;
            
        char buf[2048];
        strcpy(buf,"This is a test!!!!");
    
        fd = open("/dev/memdev0",O_RDWR);
        
        if(fd == -1)
        {
            printf("Error!!\n");
            exit(-1);
        }
        /*创建映射*/
        start = mmap(NULL,2048,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
        /*必须检测是否成功*/
        if(start == -1)
        {
            printf("mmap error!!!\n");
            exit(-1);
        }
    
        strcpy(start,buf);
    
        printf("start = %s,buf = %s\n",start,buf);
    
        strcpy(start,"Test is Test!!!\n");
    
        printf("start = %s,buf = %s\n",start,buf);
        /**/
        strcpy(buf,start);
    
        printf("start = %s,buf=%s\n",start,buf);
        /*取消映射关系*/
        munmap(start,2048);
        /*关闭文件*/
        close(fd);
    
        exit(0);
    }

    经过测试,成功得到了驱动。

 

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux下,mmap函数可以用来将文件或设备的一部分物理内存映射到进程的虚拟地址空间,从而实现进程和文件或设备的直接交互。使用mmap函数可以提高文件或设备的读写效率,避免了频繁的系统调用和缓冲区的拷贝。 mmap函数的原型为: ```c void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); ``` 各个参数的含义如下: - addr:映射区域的首地址,一般设为NULL,由系统自动分配。 - length:映射区域的长度,单位是字节。 - prot:映射区域的保护模式,可以是PROT_READ、PROT_WRITE或PROT_EXEC的组合。 - flags:映射区域的标志,可以是MAP_SHARED、MAP_PRIVATE、MAP_FIXED等的组合。 - fd:需要映射的文件描述符。 - offset:文件偏移量,表示从文件的哪个位置开始映射。 mmap函数返回映射区域的首地址或者MAP_FAILED,表示映射失败。 使用mmap函数时,需要先打开文件或设备,并获取相应的文件描述符。然后,调用mmap函数将文件或设备的一部分物理内存映射到进程的虚拟地址空间。最后,使用指针来访问映射区域的数据,进行读写操作。使用完映射区域后,需要调用munmap函数解除映射关系。 需要注意的是,使用mmap函数进行读写操作时,需要考虑到内存对齐和边界问题,否则可能会出现读写错误。同时,对于设备文件的映射,还需要考虑到设备驱动程序的特殊要求,比如缓冲区的大小和对齐方式等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值