mmap设备方法笔记

 

mmap系统调用(功能)

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

参数:

addr

指定映射的起始地址(通常不指定)通常为NULL,由系统指定

length

映射到内存的文件长度

prot

映射区的保护方式:

PROT_EXEC:映射区可被执行

PROT_READ:映射区可被读取

PROTWRITE:映射区可被写入

flags

映射区的特性:

MAP_SHARED:写入映射区的内容最后要写入文件

MAP_PRIVATE:最后不会写入文件

fd

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

offset

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

返回

会返回起始地址,本来mmap是指向内存地址的指针

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

mmap设备方法笔记_第1张图片

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

解除映射

函数原型:

int munmap(void *start, size_t length)

功能:取消参数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来描述虚拟内存区

其中几个主要成员如下:

unsigned long vm_start

虚拟内存区域起始地址

unsigned long vm_end

虚拟内存区域结束地址

unsigned long vm_flags

该区域的标记:如VM_IO  

VM_RESERVED

mmap设备操作

映射一个设备是指 把用户空间的一段地址关联到设备内存上

当程序读写这段用户空间的地址时,它实际上是在访问设备。

步骤

1)找到用户空间的地址(内核自动帮你做好)

2)找到设备的物理地址(查看芯片手册)

3)关联 (通过页式管理)

 mmap设备方法所需要做的就是建立虚拟地址到物理地址的页表

int (*mmap)(struct file * , struct vm_area-strcut *)

                               ↓

                           内核帮我找的

mmap如何完成页表的建立?

方法有二:

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

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

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

要映射的物理地址所在的物理页帧号(物理地址的序列号),可将物理地址>>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;

}

你可能感兴趣的:(struct,IO,File,null)