- /*主要是建立虚拟地址到物理地址的页表关系,其他的过程又内核自己完成*/
- 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,否则正数。
测试代码可以直接通过对虚拟内存区域操作,实现不同的操作,如下:
- #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);
- }
经过测试,成功得到了驱动。