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负责把文件内容映射到进程的虚拟内存空间,通过对这段内存的读取和修改,来实现对文件的读取和修改,而不需要再调用readwrite等操作。直接用指针操作文件的内容。

mmap设备方法笔记

图中左边的是进程的虚拟空间,右边的是文件。

解除映射

函数原型:

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位,相当于除以4k2^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; }