2012-08-02 16:06:41 dajian790626 阅读数 349

Linux提供了内存映射函数mmap, 它把文件内容映射到一段内存上(准确说是虚拟内存上), 通过对这段内存的读取和修改, 实现对文件的读取和修改, 先来看一下mmap的函数声明:

  • 头文件:
    • <unistd.h>
    • <sys/mman.h>
  • 原型: void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offsize);
  • 返回值: 成功则返回映射区起始地址, 失败则返回MAP_FAILED(-1).
  • 参数:
    • addr: 指定映射的起始地址, 通常设为NULL, 由系统指定.
    • length: 将文件的多大长度映射到内存.
    • prot: 映射区的保护方式, 可以是:
      • PROT_EXEC: 映射区可被执行.
      • PROT_READ: 映射区可被读取.
      • PROT_WRITE: 映射区可被写入.
      • PROT_NONE: 映射区不能存取.
    • flags: 映射区的特性, 可以是:
      • MAP_SHARED: 对映射区域的写入数据会复制回文件, 且允许其他映射该文件的进程共享.
      • MAP_PRIVATE: 对映射区域的写入操作会产生一个映射的复制(copy-on-write), 对此区域所做的修改不会写回原文件.
      • 此外还有其他几个flags不很常用, 具体查看linux C函数说明.
    • fd: 由open返回的文件描述符, 代表要映射的文件.
    • offset: 以文件开始处的偏移量, 必须是分页大小的整数倍, 通常为0, 表示从文件头开始映射.

    下面说一下内存映射的步骤:

  • 用open系统调用打开文件, 并返回描述符fd.
  • 用mmap建立内存映射, 并返回映射首地址指针start.
  • 对映射(文件)进行各种操作, 显示(printf), 修改(sprintf).
  • 用munmap(void *start, size_t lenght)关闭内存映射.
  • 用close系统调用关闭文件fd.

注意事项:

在修改映射的文件时, 只能在原长度上修改, 不能增加文件长度, 因为内存是已经分配好的.

 

mmap
2014-03-25 16:18:14 fengxin215 阅读数 125
mmap作为struct file_operations的重要一个元素,mmap主要是实现物理内存到虚拟内存的映射关系,这样可以实现直接访问虚拟内存,而不用使用设备相关的 read、write操作,mmap的基本过程是将文件映射到虚拟内存中。在之前的一篇博客中谈到了mmap实现文件复制的操作。
关于linux中的mmap调用如下,最好的办法查看命令,man mmap:
必要的头文件
#include
函数声明
void * mmap(void *addr,size_t length,int prot,
         int flags,int fd,off_t offset);
 
关于各个参数的意义如下:
1、返回值是一个通用型指针,这样就保证了各种类型的申请方式。
2、void *addr 是程序员所希望的虚拟地址作为起始映射地址,通常为NULL,内核自动分配。
3、size_t length当然是指需要映射的区域大小。
4、int flags是指对这段区域的保护方式。具体的可以参看内核源码的linux/mm.h。常用的是PROT_EXEC,PROT_READ,PROT_WRITE,PROT_NONE。
5、int flags主要是指对这段区域的映射方式,主要分为两种方式MAP_SHARE,MAP_PRIVATE.其中的MAP_SHARE是指对映射区域的写操 作会更新到文件中,这样就相当于直接操作文件。而MAP_PRIVATE通常采用一种称为"写时保护的机制"实现映射,对映射区的写操作不会更新到文件 中,实现方法是将需要被写操作的页复制到重新分配的新页中,然后再对新的页进行写操作。原来的映射并没有改变,但是读操作并不会重新分配物理内存空间。具 体的参考深入理解计算机系统。
6、int fd是指将被映射的文件描述符,映射需要保证文件描述符的正确性。
7、off_t offset是指从文件的具体位置开始映射,通常情况下可以设置为0,即从开头映射。
基本的映射关系如下图:
 
mmap
 
设备驱动的mmap实现主要是将一个物理设备的可操作区域(设备空间)映射到一个进程的虚拟地址空间。这样就可以直接采用指针的方式像访问内存的方式访问设备。在驱动中的mmap实现主要是完成一件事,就是实际物理设备的操作区域到进程虚拟空间地址的映射过程。同时也需要保证这段映射的虚拟存储器区域不会被进程当做一般的空间使用,因此需要添加一系列的保护方式。
 
具体的实现过程如下:
  1. /*主要是建立虚拟地址到物理地址的页表关系,其他的过程又内核自己完成*/
  2. static int mem_mmap(struct file* filp,struct vm_area_struct *vma)
  3. {
  4.     /*间接的控制设备*/
  5.     struct mem_dev *dev = filp->private_data;
  6.     
  7.     /*标记这段虚拟内存映射为IO区域,并阻止系统将该区域包含在进程的存放转存中*/
  8.     vma->vm_flags |= VM_IO;
  9.     /*标记这段区域不能被换出*/
  10.     vma->vm_flags |= VM_RESERVED;

  11.     /**/
  12.     if(remap_pfn_range(vma,/*虚拟内存区域*/
  13.         vma->vm_start, /*虚拟地址的起始地址*/
  14.         virt_to_phys(dev->data)>>PAGE_SHIFT, /*物理存储区的物理页号*/
  15.         dev->size,    /*映射区域大小*/        
  16.         vma->vm_page_prot /*虚拟区域保护属性*/    
  17.         ))
  18.         return -EAGAIN;

  19.     return 0;
  20. }
具体的实现分析如下:
  1. vma->vm_flags |= VM_IO;
  2. 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<</span>fcntl.h>
  2. #include<</span>unistd.h>
  3. #include<</span>stdio.h>
  4. #include<</span>stdlib.h>
  5. #include<</span>sys/types.h>
  6. #include<</span>sys/stat.h>
  7. #include<</span>sys/mman.h>
  8. #include<</span>string.h>

  9. int main()
  10. {
  11.     int fd;
  12.     char *start;
  13.         
  14.     char buf[2048];
  15.     strcpy(buf,"This is a test!!!!");

  16.     fd = open("/dev/memdev0",O_RDWR);
  17.     
  18.     if(fd == -1)
  19.     {
  20.         printf("Error!!\n");
  21.         exit(-1);
  22.     }
  23.    
  24.     start = mmap(NULL,2048,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
  25.    
  26.     if(start == -1)
  27.     {
  28.         printf("mmap error!!!\n");
  29.         exit(-1);
  30.     }

  31.     strcpy(start,buf);

  32.     printf("start = %s,buf = %s\n",start,buf);

  33.     strcpy(start,"Test is Test!!!\n");

  34.     printf("start = %s,buf = %s\n",start,buf);
  35.     
  36.     strcpy(buf,start);
  37.  
  38.     printf("start = %s,buf=%s\n",start,buf);
  39.    
  40.     munmap(start,2048);
  41.    
  42.     close(fd);

  43.     exit(0);
  44. }

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

2017-06-03 21:15:33 lixiaogang_theanswer 阅读数 1973

1.文件映射,能够将硬盘映射到进程的地址,这样可以向操作内存一样来操作文件,而且效率高;但是有一定的限制,
· 文件的长度必须大于等于映射的长度;
· 映射的offset必须是页(page)的整数倍;

注意:映射不会增长文件的长度;映射部分的内容应该是文件本来就应该有的内容;

2 .文件映射主要使用到两个函数;分别是:mmap和munmap;其函数的原型和所需头文件如下:

 #include <sys/mman.h>

 void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset); 
 参数:addr通常都指定为NULL,让操作系统来决定在内存空间中开辟映射的空间;
     length:为所映射的长度;
     prot:表示protect,该参数描述了映射的所需内存保护;并且不能与文件的开放模式发生冲突)。
     flags: MAP_SHARED才会将内存体现在硬盘, MAP_PRIVATE不会将数据保存到硬盘
     fd:文件描述符
     offset:从文件的哪部分开始映射

 int munmap(void *addr, size_t length);
 munmap释放内存中分配的映射内存空间;

这里写图片描述

                  文件映射示意图

3 .在当前的目录下面创建(touch)一个文件1.txt,然后使用命令将其文件空间大小改为“文件映射时候的length”大小;当然这里的length大小可以改变,但是值得注意的是当length改变的时候,文件1.txt文件的大小也得跟着改变,不然执行时候,会出现“段错误”;

改变文件大小方式有两种:

(1) truncate 1.txt --size=4096
也可以采用ftruncate,ftruncate和truncate两个函数的功能都相同,只不过ftruncate函数是通过文件的全路径;而truncate是通过文件描述符来达到目的。

(2) dd if=/dev/zero of= 1.txt bs = 1024 count = 4
/*************************************************************************
    * File Name: mmap.c
    * Author: lixiaogang
    * Mail: 2412799512@qq.com 
    * Created Time: 2017年06月03日 星期六 20时16分31秒
 ************************************************************************/

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/mman.h>
#include<sys/types.h>
#include<fcntl.h>

int main(int argc,char *argv[])
  {
    int fd = open("1.txt",O_RDWR);
    char buf[] = "hello world.";
    if(fd < 0){
        perror("open");
        return -1;
    }

    size_t length = 4096;
    char *ptr =(char*) mmap(NULL,length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    if(NULL ==ptr){
        perror("mmap");
        return -1;
    }
    strcpy(ptr,buf);
    munmap(ptr,length);
    close(fd);
/*注意,这里必须得指定文件1.txt的size大小为      *length=4096的大小,不然会出现段错误;
    //因为mmap内存映射不会像文件读写一样,自动地去增长文件空间的大小
    return 0;
  }

打开文件1.txt,此时文件中的内容是

hello world.

4 .若将该代码中的MAP_SHARED改为MAP_PRIVATE,则不会将hello wrold写入1.txt文件中,也就是说不会写入到硬盘里面。

char *ptr =(char*) mmap(NULL,length,PROT_READ|PROT_WRITE,MAP_PRIVATE,fd,0);
2007-07-25 17:09:00 dai_weitao 阅读数 20807

    Linux提供了内存映射函数mmap, 它把文件内容映射到一段内存上(准确说是虚拟内存上), 通过对这段内存的读取和修改, 实现对文件的读取和修改, 先来看一下mmap的函数声明:

  • 头文件:
    • <unistd.h>
    • <sys/mman.h>
  • 原型: void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offsize);
  • 返回值: 成功则返回映射区起始地址, 失败则返回MAP_FAILED(-1).
  • 参数:
    • addr: 指定映射的起始地址, 通常设为NULL, 由系统指定.
    • length: 将文件的多大长度映射到内存.
    • prot: 映射区的保护方式, 可以是:
      • PROT_EXEC: 映射区可被执行.
      • PROT_READ: 映射区可被读取.
      • PROT_WRITE: 映射区可被写入.
      • PROT_NONE: 映射区不能存取.
    • flags: 映射区的特性, 可以是:
      • MAP_SHARED: 对映射区域的写入数据会复制回文件, 且允许其他映射该文件的进程共享.
      • MAP_PRIVATE: 对映射区域的写入操作会产生一个映射的复制(copy-on-write), 对此区域所做的修改不会写回原文件.
      • 此外还有其他几个flags不很常用, 具体查看linux C函数说明.
    • fd: 由open返回的文件描述符, 代表要映射的文件.
    • offset: 以文件开始处的偏移量, 必须是分页大小的整数倍, 通常为0, 表示从文件头开始映射.

 

    下面说一下内存映射的步骤:

  • 用open系统调用打开文件, 并返回描述符fd.
  • 用mmap建立内存映射, 并返回映射首地址指针start.
  • 对映射(文件)进行各种操作, 显示(printf), 修改(sprintf).
  • 用munmap(void *start, size_t lenght)关闭内存映射.
  • 用close系统调用关闭文件fd.

 

注意事项:

在修改映射的文件时, 只能在原长度上修改, 不能增加文件长度, 因为内存是已经分配好的.

2015-06-05 00:14:55 dcx1205 阅读数 667

Linux mmap函数简介

 

Linux提供了内存映射函数mmap, 它把文件内容映射到一段内存上(准确说是虚拟内存上), 通过对这段内存的读取和修改, 实现对文件的读取和修改, 先来看一下mmap的函数声明:

  • 头文件:
    • <unistd.h>
    • <sys/mman.h>
  • 原型: void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offsize);
  • 返回值: 成功则返回映射区起始地址, 失败则返回MAP_FAILED(-1).
  • 参数:
    • addr: 指定映射的起始地址, 通常设为NULL, 由系统指定.
    • length: 将文件的多大长度映射到内存.
    • prot: 映射区的保护方式, 可以是:
      • PROT_EXEC: 映射区可被执行.
      • PROT_READ: 映射区可被读取.
      • PROT_WRITE: 映射区可被写入.
      • PROT_NONE: 映射区不能存取.
    • flags: 映射区的特性, 可以是:
      • MAP_SHARED: 对映射区域的写入数据会复制回文件, 且允许其他映射该文件的进程共享.
      • MAP_PRIVATE: 对映射区域的写入操作会产生一个映射的复制(copy-on-write), 对此区域所做的修改不会写回原文件.
      • 此外还有其他几个flags不很常用, 具体查看linux C函数说明.
    • fd: 由open返回的文件描述符, 代表要映射的文件.
    • offset: 以文件开始处的偏移量, 必须是分页大小的整数倍, 通常为0, 表示从文件头开始映射.

    下面说一下内存映射的步骤:

  • 用open系统调用打开文件, 并返回描述符fd.
  • 用mmap建立内存映射, 并返回映射首地址指针start.
  • 对映射(文件)进行各种操作, 显示(printf), 修改(sprintf).
  • 用munmap(void *start, size_t lenght)关闭内存映射.
  • 用close系统调用关闭文件fd.

注意事项:

在修改映射的文件时, 只能在原长度上修改, 不能增加文件长度, 因为内存是已经分配好的.


转载处链接:
http://blog.sina.com.cn/s/blog_54f82cc201010op6.html
没有更多推荐了,返回首页