mmap设备方法笔记
mmap系统调用(功能)
void*mmap(void*addr,size_tlen,intprot,intflags,intfd,off_toffset)
参数:
addr |
指定映射的起始地址(通常不指定)通常为NULL,由系统指定 |
length |
映射到内存的文件长度 |
prot |
映射区的保护方式: PROT_EXEC:映射区可被执行 PROT_READ:映射区可被读取 PROTWRITE:映射区可被写入 |
flags |
映射区的特性: MAP_SHARED:写入映射区的内容最后要写入文件 MAP_PRIVATE:最后不会写入文件 |
fd |
由open返回的文件描述符,代表要映射的文件 |
offset |
以文件开始处的偏移量,必须是分布大小的整数倍,通常为0,表示从文件头开始映射 |
返回 |
会返回起始地址,本来mmap是指向内存地址的指针 |
内存映射函数mmap,负责把文件内容映射到进程的虚拟内存空间,通过对这段内存的读取和修改,来实现对文件的读取和修改,而不需要再调用read、write等操作。直接用指针操作文件的内容。
图中左边的是进程的虚拟空间,右边的是文件。
解除映射
函数原型:
int munmap(void*start,size_tlength)
功能:取消参数start所指向的映射内存,参数length表示要取消的内存大小
返回值:解除成功返回0,否则返回-1,错误原因存于errno中。
#include <stdio.h> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<unistd.h> #include<sys/mman.h> int main() { int fd; char *start; //char buf[100]; char *buf; /*打开文件*/ fd = open("/dev/memdev0",O_RDWR); buf = (char *)malloc(100); memset(buf, 0, 100); start=mmap(NULL,100,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); /* 读出数据 */ strcpy(buf,start); sleep (1); printf("buf 1 = %s\n",buf); /* 写入数据 */ strcpy(start,"Buf Is Not Null!"); memset(buf, 0, 100); strcpy(buf,start); sleep (1); printf("buf 2 = %s\n",buf); munmap(start,100); /*解除映射*/ free(buf); close(fd); return 0; }
注意:当写入时,mmap不会影响文件的长度!
虚拟内存区域
虚拟内存区域是进程的虚拟地址空间中的一个同质区间,即具有同样特性的连续地址范围。
一个进程的内存映象由下面几部分组分:程序代码、数据、BSS和栈区域,以及内存映射的区域。通过/proc/pid/maps可以看到
VM_AREA_STRUCT
Linux内核使用结构vm_area_struct来描述虚拟内存区
其中几个主要成员如下:
unsignedlongvm_start
虚拟内存区域起始地址
unsignedlongvm_end
虚拟内存区域结束地址
unsignedlongvm_flags
该区域的标记:如VM_IO
VM_RESERVED
mmap设备操作
映射一个设备是指把用户空间的一段地址关联到设备内存上
当程序读写这段用户空间的地址时,它实际上是在访问设备。
步骤
1)找到用户空间的地址(内核自动帮你做好)
2)找到设备的物理地址(查看芯片手册)
3)关联(通过页式管理)
mmap设备方法所需要做的就是建立虚拟地址到物理地址的页表
int(*mmap)(structfile*,structvm_area-strcut*)
↓
内核帮我找的
mmap如何完成页表的建立?
方法有二:
1)使用remap_pfn_range一次建立所有页表
2)使用nopageVMA方法每次建立一个页表
intremap_pfn_range(structvm_area_struct*vma,unsignedlongaddr,unsignedlongpfn,unsignedlongsize,pgprot_tprot)
vma |
虚拟内存区域指针 |
virt_addr |
虚拟地址的起始值 |
pfn |
要映射的物理地址所在的物理页帧号(物理地址的***),可将物理地址>>PGE_SHIFT得到,即右移12位,相当于除以4k(2^12) |
prot |
VMA的保护属性 |
例子:
static int memdev_mmap(struct file*filp, struct vm_area_struct *vma) { struct mem_dev *dev = filp->private_data; /*获得设备结构体指针*/ /*设置保护属性*/ 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, vma->vm_end - vma->vm_start, vma->vm_page_prot))//关联,建立页表 return -EAGAIN; return 0; }