精华内容
下载资源
问答
  • 2022-04-10 22:19:27





    一、内存映射概念



    内存映射 概念 : " 内存映射 “ 就是在 进程的 ” 用户虚拟地址空间 " 中 , 创建一个 映射 , " 内存映射 " 有 2 2 2 种情况 , ① 文件映射 , ② 匿名映射 ;

    • 文件映射 :文件 支持 的 内存映射 , 将 指定文件 的 指定位置 指定大小 的数据 , 映射到 进程 " 用户虚拟地址空间 " 中 , 文件内容直接装载到该 虚拟内存 中 ;
    • 匿名映射 : 没有 文件 支持 的 内存映射 , 只是将 " 物理内存空间 “ 映射到 ” 虚拟内存空间 " , 其中的数据是随机值 ;




    二、内存映射原理




    1、分配虚拟内存页


    分配 虚拟内存页 : 在 Linux 系统中 创建 " 内存映射 “ 时 , 会在 ” 用户虚拟地址空间 “ 中 , 分配一块 ” 虚拟内存区域 " ;


    2、产生缺页异常


    缺页异常 : Linux 内核在分配 " 物理内存 “ 时 , 采用了 ” 延迟策略 “ , 即进程第一次访问 , 不会立即分配 物理内存 , 而是产生一个 ” 缺页异常 " ;


    3、分配物理内存页


    分配 物理内存页 : 缺页异常后的 2 2 2 种处理策略 ;

    • 文件映射 : 对于 " 文件映射 " , 遇到 " 缺页异常 " 后 , 会 分配 " 物理内存页 “ , 并且将 要映射的文件 的 部分数据 读取到 该 ” 物理内存页 " 中 ;
    • 匿名映射 : 对于 " 匿名映射 " , 直接分配 " 物理内存页 “ , 并且在 " 页表 “ 中 , 将 ” 虚拟内存页 " 映射到 ” 物理内存页 " ;




    三、共享内存



    内存映射 与 共享内存 关系 :

    • 文件映射 : 在进程间的 " 共享内存 " 就是使用 共享的 " 文件映射 " 实现的 ;

    • 匿名映射 : " 匿名映射 “ 一般是 ” 私有映射 " , 一般不作为 " 共享内存 " 使用 , 如果两个进程之间 共享 匿名映射 , 只能是 父子进程之间 才可以 ;


    如果修改了 进程间的 " 共享内存 " 对应的 " 文件映射 " , 修改后不会立刻更新到文件中 , 调用 msync 函数 , 强制同步写入到文件中 ;





    四、进程内存段的内存映射类型



    在 进程 的 " 用户虚拟地址空间 " 中 , 不同的 内存段 其 内存映射 类型也是不同的 :

    • 代码段 : 私有的 " 文件映射 " ;
    • 数据段 : 私有的 " 文件映射 " ;
    • 未初始化数据段 : 私有的 " 匿名映射 " ;
    • 堆栈 : 私有的 " 匿名映射 " ;
    更多相关内容
  • 内存映射文件原理 首先说说这篇文章要解决什么问题? 1.虚拟内存内存映射文件的区别与联系. 2.内存映射文件原理. 3.内存映射文件的效率. 4.传统IO和内存映射效率对比. 虚拟内存内存映射文件的区别与联系  ...
  • 内存映射文件原理

    2020-05-11 17:55:44
    内存映射文件内存映射文件与虚拟内存的区别映射原理mmap() 函数mmap基础概念mmap映射原理mmap优点mmap相关函数 内存映射文件与虚拟内存的区别   内存映射文件是由操作系统支持的一种文件处理方式,通过文件映射,...

    内存映射文件与虚拟内存的区别

      内存映射文件是由操作系统支持的一种文件处理方式,通过文件映射,让用户处理磁盘文件时就如同操作内存一样。再处理大文件时,效率比传统IO高很多。
      虚拟内存和内存映射文件都是将一部分文件加载到内存,另一部分文件保存在磁盘上的一种机制。但是二者是有区别的:

    1. 架构不同,虚拟内存是构建在物理内存之上的,引入原因是实际物理内存无法满足程序所需的内存空间;内存映射文件架构在进程地址空间之上,引入原因是无法将整个大文件全部加载到内存空间
    2. 虚拟内存管理的是页面文件,内存映射文件可以是任意磁盘文件。

    映射原理

      映射其实就是在虚拟地址空间和磁盘地址空间建立了一个一一对应关系,在逻辑地址空间为文件复配一个大小相等的逻辑空间。在文件映射过程中并没有数据拷贝,文件并没有放入内存,只是逻辑上放入了内存中,具体到代码就是建立了初始化数据结构,这个过程由mmap()系统调用实现的,所以映射效率非常高。
      映射过程如下:

    1. 调用mmap()函数,相当于给磁盘文件分配虚拟内存空间,它返回一个指针ptr,这个指针指向的是一个逻辑地址,操作系统要操作其中的数据必须通过MMU翻译才能获取对应的内存物理地址。
    2. 建立内存映射并没有数据拷贝,这时通过MMU翻译ptr是无法找到与之对应的内存物理地址的,也就是MMU失败,产生一个缺页中断,缺页中断响应函数会在磁盘的交换区查找相应页面,如果找不到证明文件还未读入磁盘,则通过mmp()建立的映射关系将文件加载到物理内存中,如果在交换区找到对应文件则换入
    3. 如果在数据拷贝过程中发现内存不够用,则通过虚拟内存管理将部分页面数据换出到交换区。

    mmap() 函数

    mmap基础概念

      mmap是一种内存映射文件的方法,是将文件或者其他对象映射到进程的地址空间中去,实现磁盘地址和虚拟空间地址一一对映关系,实现这样的映射关系后,进程就可以采用指针的方式操作这段内存,而系统会将修改后的脏页面自动写回磁盘中去。使用内存映射文件时,不用调用read,write等系统调用函数。内核空间对映射区域内容进行修改也能反映到用户空间,从而实现文件共享。
    在这里插入图片描述
      进程的虚拟地址空间由多个虚拟内存区域组成,虚拟内存区域是进程虚拟地址空间的一个同质区域,及具有同样特性的连续地址范围,上图中的text数据段、初始化数据段、bss数据段等都是一个独立的虚拟地址区域,内存映射文件地址空间处于堆、栈地址空间之间的空余部分。inux内核使用vm_area<struct结构来表示一个独立的虚拟内存区域,每个不同质的虚拟内存区域功能和内部机制都不一样,因此一个进程有多个vm_area_struct数据结构组成,每个数据结构使用链表相连,如下图(以上两图来自于此博文

    在这里插入图片描述
      vm_area_struct结构包含了区域起始地址和其他信息,mmap()函数就是创建了一个vm_area_struct结构,并将其与磁盘地址相连。

    mmap映射原理

      mmap映射分为三个阶段:

    (一)启动映射过程,创建一个虚拟映射区域,封装成vm_area_struct结构

    1. 进程调用用户空间库函数void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset),在当前进程的虚拟空间找以一段连续的大小满足被映射文件大小要求的虚拟地址区域

    2. 为此虚拟地址区域分配一个vm_area_struct结构,并初始化该结构

    3. 将该结构插入进程的虚拟地址区域链中
      (二)调用内核空间的系统调用函数mmap(不同于用户空间的mmap库函数),实现物理地址与虚拟地址一一映射关系

    4. 通过内核函数int mmap(struct file *filp, struct vm_area_struct *vma)建立虚拟地址与磁盘地址的一一映射关系

    (三)进程发起对映射空间的访问,产生缺页中断,中断函数实现从磁盘拷贝到物理内存上

    1. 进程操作文件发现数据页没在内存中则到交换区中查找,如果找不到证明数据还未加载,因此内核直接将磁盘数据复制到用户空间中,实现高效拷贝

    mmap优点

    1. 对文件的读取操作跨过了页缓存,减少了数据的拷贝次数,用内存读写取代I/O读写,提高了文件读取效率
    2. 实现了用户空间和内核空间的高效交互方式。两空间的各自修改操作可以直接反映在映射的区域内,从而被对方空间及时捕捉
    3. 提供进程间共享内存及相互通信的方式。不管是父子进程还是无亲缘关系的进程,都可以将自身用户空间映射到同一个文件或匿名映射到同一片区域。从而通过各自对映射区域的改动,达到进程间通信和进程间共享的目的
    4. 可用于实现高效的大规模数据传输。内存空间不足,是制约大数据操作的一个方面,解决方案往往是借助硬盘空间协助操作,补充内存的不足。但是进一步会造成大量的文件I/O操作,极大影响效率。这个问题可以通过mmap映射很好的解决。换句话说,但凡是需要用磁盘空间代替内存的时候,mmap都可以发挥其功效

    mmap相关函数

    1. void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
      成功执行时,mmap()返回被映射区的指针。失败时,mmap()返回MAP_FAILED[其值为(void *)-1]
      参数介绍:
      start:映射区的开始地址
      length:映射区的长度
      prot: 期望的内存保护标志
      fd:有效的文件描述词
      offset:被映射对象内容的起点

    2. int munmap( void * addr, size_t len )
      成功执行时,munmap()返回0。失败时,munmap返回-1。该调用在进程地址空间中解除一个映射关系,addr是调用mmap()时返回的地址,len是映射区的大小;

    参考:
    参考1
    参考2

    展开全文
  • 通过两个线程,实现两个独立进程的相互通信。通过控制台输入输出。 http://blog.csdn.net/huanglong8/article/details/53954601
  • 内存文件映射原理和简单应用

    千次阅读 2017-08-06 19:22:39
    参考博客:... ... 内存映射文件原理探究 硬盘上文件 的位置与进程 逻辑地址空间 中一块大小相同的区域之间的一一对应,这种对应关系纯属是逻辑上的概念,物理上是不存在的,原因是
    1. 参考博客:http://blog.csdn.net/haiross/article/details/46875211
    2. 参考博客:http://blog.csdn.net/mg0832058/article/details/5890688 内存映射文件原理探究
    3. 硬盘上文件 的位置与进程 逻辑地址空间 中一块大小相同的区域之间的一一对应,这种对应关系纯属是逻辑上的概念,物理上是不存在的,原因是进程的逻辑地址空间本身就是不存在的,在内存映射的过程中,并没有实际的数据拷贝,文件没有被载入内存,只是逻辑上被放入了内存,具体到代码,就是建立并初始化了相关的数据结构(struct address_space),这个过程有系统调用mmap()实现,所以建立内存映射的效率很高。mmap将一个文件或者其它对象映射进内存,mmap必须以PAGE_SIZE为单位进行映射,而内存也只能以页为单位进行映射,若要映射非PAGE_SIZE整数倍的地址范围,要先进行内存对齐,强行以PAGE_SIZE的倍数大小进行映射。
    4. mmap()会返回一个指针ptr,它指向进程逻辑地址空间中的一个地址,这样以后,进程无需再调用read或write对文件进行读写,而只需要通过ptr就能够操作文件。但是ptr所指向的是一个逻辑地址,要操作其中的数据,必须通过MMU(内存管理单元)将逻辑地址转换成物理地址,这个过程与内存映射无关。

      前面讲过,建立内存映射并没有实际拷贝数据,这时,MMU在地址映射表中是无法找到与ptr相对应的物理地址的,也就是MMU失败,将产生一个缺页中断,缺页中断的中断响应函数会在swap中寻找相对应的页面,如果找不到(也就是该文件从来没有被读入内存的情况),则会通过mmap()建立的映射关系,从硬盘上将文件读取到物理内存中,这个过程与内存映射无关。

       如果在拷贝数据时,发现物理内存不够用,则会通过虚拟内存机制(swap)将暂时不用的物理页面交换到硬盘上,如图1中过程4所示。这个过程也与内存映射无关。

    5. 效率:从代码层面上看,从硬盘上将文件读入内存,都要经过文件系统进行数据拷贝,并且数据拷贝操作是由文件系统和硬件驱动实现的,理论上来说,拷贝数据的效率是一样的。但是通过内存映射的方法访问硬盘上的文件,效率要比read和write系统调用高,这是为什么呢?原因是read()是系统调用其中进行了数据拷贝,它首先将文件内容从硬盘拷贝到内核空间的一个缓冲区,然后再将这些数据拷贝到用户空间,在这个过程中,实际上完成了 两次数据拷贝 ;而mmap()也是系统调用,如前所述,mmap()中没有进行数据拷贝,真正的数据拷贝是在缺页中断处理时进行的,由于mmap()将文件直接映射到用户空间,所以中断处理函数根据这个映射关系,直接将文件从硬盘拷贝到用户空间,只进行了 一次数据拷贝 。因此,内存映射的效率要比read/write效率高。

    6. 创建文件映射示例:
    7. #include <iostream>  
    8. #include <fcntl.h>  
    9. #include <io.h>  
    10. #include <afxwin.h>  
    11. using namespace std;  
    12.   
    13. int main()  
    14. {  
    15.     //开始  
    16. //1.获得文件句柄  创建一个文件内核对象
    17.     HANDLE hFile=CreateFile(              //失败返回INVALID_HANDLE_VALUE    
    18.         "c:\\test.dat",                   //文件名  
    19.         GENERIC_READ|GENERIC_WRITE,       //对文件进行读写操作,文件权限,
    20.         FILE_SHARE_READ|FILE_SHARE_WRITE, //共享模式
    21.         NULL,                             //安全属性
    22.         OPEN_EXISTING,                    //打开已存在文件  
    23.         FILE_ATTRIBUTE_NORMAL,            //文件或设备属性或标志
    24.         NULL);                               //模板模式
    25.   
    26.     //返回值size_high,size_low分别表示文件大小的高32位/低32位  
    27.     DWORD size_low,size_high;  
    28.     size_low= GetFileSize(hFile,&size_high);   
    29. //2.创建文件的内存映射文件, 创建一个文件映射内核对象
    30.     HANDLE hMapFile=CreateFileMapping   
    31.         hFile,           //文件名  
    32.         NULL,            //安全属性
    33.         PAGE_READWRITE,  //对映射文件进行读写  
    34.         size_high,      
    35.         size_low,        //这两个参数共64位,所以支持的最大文件长度为16EB  ,一共要映射多少到内存中
    36.         L"MyFileMap");   //映射的文件名字,其他进程,可通过此名字打开共享文件  
    37.     if(hMapFile==NULL)     //失败返回NULL
    38.     {     
    39.         AfxMessageBox("Can't create file mapping.Error%d:\n",   GetLastError());     
    40.         CloseHandle(hFile);  
    41.         return 0;     
    42.     }    
    43. //3.把文件数据映射到进程的地址空间  
    44.     void* pvFile=MapViewOfFile(       //失败返回NULL
    45.         hMapFile,                     //文件映射的句柄
    46.         FILE_MAP_READ|FILE_MAP_WRITE, //权限
    47.         0,                            //高位偏移
    48.         0,                            //低位偏移    
    49.         0);                           //一次映射多少 64K,如果是0,则映射从偏移量到文件末尾。
    50.     unsigned char *p=(unsigned char*)pvFile;   
    51.   
    52.     //至此,就获得了外部文件test.dat在内存地址空间的映射,  
    53. //4.用指针p"折磨"这个文件了  
    54.     CString s;  
    55.     p[size_low-1]='!';   
    56.     p[size_low-2]='X'//修改该文件的最后两个字节(文件大小<4GB高32位为0)  
    57.     s.Format("%s",p);  
    58.     //读文件的最后3个字节  
    59.     AfxMessageBox(s);  
    60. //5.结束  
    61.     //UnmapViewOfFile(pvFile); //撤销映射  
    62.     //CloseHandle(hFile); //关闭文件   
    63.     return 0;  
    64. }
    65. 打开文件映射示例:
    66. #include<iostream>
      #include<Windows.h>
      using namespace std;
      int main()
      {
      char szBuf[100];
      //1.打开文件映射对象
      //权限,继承性,名字
      HANDLE hFileMap = OpenFileMapping(FILE_MAP_ALL_ACCESS,NULL,L"MyFileMap");
      if(NULL == hFileMap) return 0;
      //2.将文件映射到进程地址空间
      char * pStartAddress = (char*)MapViewOfFile(hFileMap,FILE_MAP_ALL_ACCESS,0,0,0);
      //3.取出文件内容
      memcpy(szBuf,pStartAddress,100);
      //4.取消映射
      UnmapViewOfFile(hFileMap);
      system("pause");
      return 0;
      }
    展开全文
  • 内存映射原理

    2021-12-05 22:26:04
    文件映射:文件支持的内存映射,把文件的一个区间映射到进程的虚拟地址空间,数据源是存储设备上的文件。 匿名映射:没有文件支持的内存映射,把物理内存映射到进程的虚拟地址空间,没有数据源。 创建内存映射时,...

    内存映射原理

    a. 内存映射原理

    ​ 内存映射即在进程的虚拟地址空间中创建一个映射,分为两种:

    • 文件映射:文件支持的内存映射,把文件的一个区间映射到进程的虚拟地址空间,数据源是存储设备上的文件。
    • 匿名映射:没有文件支持的内存映射,把物理内存映射到进程的虚拟地址空间,没有数据源。

    创建内存映射时,在进程的用户虚拟地址空间中分配一个虚拟内存区域。内核采用延迟分配物理内存的策略,在进程第一次访问虚拟页的时候,产生缺页异常。如果是文件映射,那么分配物理页,把文件指定区间的数据读到物理页中,然后在页表中把虚拟页映射到物理页。
    如果是匿名映射,就分配物理页,然后在页表中把虚拟页映射到物理页。

    内核必须提供数据结构,以建立虚拟地址空间的区域和相关数据所在位置之间的关联。例如,在映射文本文件时,映射的虚似内存区必须关联到文件系统在硬盘上存储文件内容的区域。

    在这里插入图片描述

    当然,给出的图示是简化的,因为文件数据在硬盘上的存储通常并不是连续的,而是分布到若干小的区域。内核利用address_space数据结构,提供一组方法从后备存储器读取数据。例如,从文件系统读取。因此address_space形成了一个辅助层,将映射的数据表示为连续的线性区域,提供给内存管理子系统。按需分配和填充页称之为按需调页法( demand paging)。它基于处理器和内核之间的交互,使用的各种数据结构如图。

    在这里插入图片描述

    • 进程试图访问用户地址空间中的一个内存地址,但使用页表无法确定物理地址(物理内存中没有关联页)。

    • 处理器接下来触发一个缺页异常,发送到内核。

    • 内核会检查负责缺页区域的进程地址空间数据结构,找到适当的后备存储器,或者确认该访问实际上是不正确的。

    • 分配物理内存页,并从后备存储器读取所需数据填充。

    • 借助于页表将物理内存页并入到用户进程的地址空间,应用程序恢复执行。

    这些操作对用户进程是透明的。换句话说,进程不会注意到页是实际在物理内存中,还是需要通过按需调页加载。

    b.数据结构

    虚拟内存区域分配给进程的一个虚拟地址范围,内核使用结构体vm_area_struct描述虚拟内存区域,主要核心成员如下:

    我们知道struct mm_struct很重要,该结构提供了进程在内存中布局的所有必要信息。另外,它还包括下列成员,用于管理用户进程在虚拟地址空间中的所有内存区域。

    /*mm_types.h*/
    struct mm_struct {
    	struct vm_area_struct *mmap;		/* list of VMAs */
    	struct rb_root mm_rb;
    	u32 vmacache_seqnum;                   /* per-thread vmacache */
    	.....
    }
    

    用户虚拟地址空间中的每个区域由开始和结束地址描述。现存的区域按起始地址以递增次序被归入链表中。扫描链表找到与特定地址关联的区域,在有大量区域时是非常低效的操作(数据密集型的应用程序就是这样)。因此vm_area_struct的各个实例还通过红黑树管理,可以显著加快扫描速度。增加新区域时,内核首先搜索红黑树,找到刚好在新区域之前的区域。因此,内核可以向树和线性链表添加新的区域,而无需扫描链表。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zknBf3AO-1638714343843)(C:\Users\wangzhen\AppData\Roaming\Typora\typora-user-images\image-20211205101738426.png)]

    b.1 虚拟内存区域的数据结构

    每个区域表示为vm_area_struct的一个实例,其定义(简化形式)如下:

    /*
     * This struct defines a memory VMM memory area. There is one of these
     * per VM-area/task.  A VM area is any part of the process virtual memory
     * space that has a special rule for the page-fault handlers (ie a shared
     * library, the executable area etc).
     */
    struct vm_area_struct {
    	/* The first cache line has the info for VMA tree walking. */
    //这两个成员分别用来保存该虚拟内存空间的首地址和末地址后的第一个字节的地址
    	unsigned long vm_start;		/* Our start address within vm_mm. */
    	unsigned long vm_end;		/* The first byte after our end address
    					   within vm_mm. */
    
    	/* linked list of VM areas per task, sorted by address */
    	struct vm_area_struct *vm_next, *vm_prev;   //各进程的虚拟内存区域链表,按地址排序
    
    	struct rb_node vm_rb; //采用 红黑树(每个进程结构体mm_struct中都创建一颗红黑树,将VMA作为一个节点加入到红黑树中,提升搜索速度)
    
    	/*
    	 * Largest free memory gap in bytes to the left of this VMA.
    	 * Either between this VMA and vma->vm_prev, or between one of the
    	 * VMAs below us in the VMA rbtree and its ->vm_prev. This helps
    	 * get_unmapped_area find a free area of the right size.
    	 */
    	unsigned long rb_subtree_gap;
    
    	/* Second cache line starts here. */
    //指向内存描述符,即虚拟内存区域所属的用户虚拟地址空间
    	struct mm_struct *vm_mm;	/* The address space we belong to. */
    //保护位,即访问权限
    	pgprot_t vm_page_prot;		/* Access permissions of this VMA. */
        /*
        #define VM_READ           0x00000001
        #define VM_WRITE		 0x00000002
        #define VM_EXEC           0x00000004
        #define VM_SHARED         0x00000008
        */
    	unsigned long vm_flags;		/* Flags, see mm.h. */
    
    	/*
    	 * For areas with an address space and backing store,
    	 * linkage into the address_space->i_mmap interval tree.
    	 */
    	struct {
    		struct rb_node rb;
    		unsigned long rb_subtree_last;
    	} shared;
    
    	/*
    	 * A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma
    	 * list, after a COW of one of the file pages.	A MAP_SHARED vma
    	 * can only be in the i_mmap tree.  An anonymous MAP_PRIVATE, stack
    	 * or brk vma (with NULL file) can only be in an anon_vma list.
    	 */
        //把虚拟内存区域关联的所有的anon_vma实例串联起来,一个虚拟内存区域会关联到父进程的anon_vma实例和自己的anon_vma实例
    	struct list_head anon_vma_chain; /* Serialized by mmap_sem &
    					  * page_table_lock */
        //指向一个anon_vma实例,结构anon_vma用来组织匿名页被映射到的所有的虚拟地址空间
    	struct anon_vma *anon_vma;	/* Serialized by page_table_lock */
    
    	/* Function pointers to deal with this struct. */
        /*
        <mm.h>
        struct vm_operations_struct {
    	void (*open)(struct vm_area_struct * area);
    	void (*close)(struct vm_area_struct * area);
    	int (*mremap)(struct vm_area_struct * area);
    	int (*fault)(struct vm_area_struct *vma, struct vm_fault *vmf);
    	int (*pmd_fault)(struct vm_area_struct *, unsigned long address, pmd_t *, unsigned int flags);
    	void (*map_pages)(struct vm_area_struct *vma, struct vm_fault *vmf);
    	int (*page_mkwrite)(struct vm_area_struct *vma, struct vm_fault *vmf);
    	int (*pfn_mkwrite)(struct vm_area_struct *vma, struct vm_fault *vmf);
    	int (*access)(struct vm_area_struct *vma, unsigned long addr, void *buf, int len, int write);
    	const char *(*name)(struct vm_area_struct *vma);
    	struct page *(*find_special_page)(struct vm_area_struct *vma, unsigned long addr);
        */
    	const struct vm_operations_struct *vm_ops;
    
    	/* Information about our backing store: */
        //文件偏移,单位是页
    	unsigned long vm_pgoff;		/* Offset (within vm_file) in PAGE_SIZE
    					   units, *not* PAGE_CACHE_SIZE */
    	struct file * vm_file;		/* File we map to (can be NULL). */
    	void * vm_private_data;		/* was vm_pte (shared mem) 指向内存区的私有数据*/
    
    #ifndef CONFIG_MMU
    	struct vm_region *vm_region;	/* NOMMU mapping region */
    #endif
    #ifdef CONFIG_NUMA
    	struct mempolicy *vm_policy;	/* NUMA policy for the VMA */
    #endif
    	struct vm_userfaultfd_ctx vm_userfaultfd_ctx;
    };
    

    c. 系统调用和mmap内存映射

    c.1 系统调用

    应用程序通常使用C标准库提供的函数malloc()申请内存,glibc库的内存分配器ptmalloc使用brk或mmap向内核以页为单位申请虚拟内存,然后把页划分成小内存块分配给应用程序。默认的阈值时128kb,如果应用程序申请的内存长度小于阈值,ptmalloc分配器使用brk向内核申请虚拟内存,否则ptmalloc分配器使用mmap向内核申请虚拟内存。应用程序可以直接使用mmap向内核申请虚拟内存。

    我们已经熟悉了内存映射相关的数据结构和地址空间操作,在本节中,我们将进一步讨论在建立映射时内核和应用程序之间的交互。就我们所知, C标准库提供了mmap 函数建立映射。在内核一端,提供了两个系统调用mmapmmap2。两个函数的参数相同。

    void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);
    

    这两个调用都会在用户虚拟地址空间中的pos位置,建立一个长度为len的映射,其访问权限通过prot定义。 flags是一个标志集,用于设置一些参数。相关的文件通过其文件描述符fd标识。

    mmapmmap2之间的差别在于偏移量的语义( off)。在这两个调用中,它都表示映射在文件中开始的位置。对于mmap,位置的单位是字节,而mmap2使用的单位则是页( PAGE_SIZE)。因此即使文件比可用地址空间大,也可以映射文件的一部分。通常C标准库只提供一个函数,由应用程序用来创建内存映射。接下来该函数调用在内部转换为适合于体系结构的系统调用。可使用munmap系统调用删除映射。因为不需要文件偏移量,因此不需要munmap2系统调用,只需提供映射的虚拟地址。

    参数start:指向欲映射的内存起始地址,通常设为 NULL,代表让系统自动选定地址,映射成功后返回该地址。

    参数length:代表将文件中多大的部分映射到内存。

    参数prot:映射区域的保护方式。可以为以下几种方式的组合:
    PROT_EXEC 映射区域可被执行
    PROT_READ 映射区域可被读取
    PROT_WRITE 映射区域可被写入
    PROT_NONE 映射区域不能存取

    参数flags:影响映射区域的各种特性。在调用mmap()时必须要指定MAP_SHARED 或MAP_PRIVATE。

    MAP_FIXED 如果参数start所指的地址无法成功建立映射时,则放弃映射,不对地址做修正。通常不鼓励用此旗标。
    MAP_SHARED对映射区域的写入数据会复制回文件内,而且允许其他映射该文件的进程共享。
    MAP_PRIVATE 对映射区域的写入操作会产生一个映射文件的复制,即私人的“写入时复制”(copy on write)对此区域作的任何修改都不会写回原来的文件内容。
    MAP_ANONYMOUS建立匿名映射。此时会忽略参数fd,不涉及文件,而且映射区域无法和其他进程共享。
    MAP_DENYWRITE只允许对映射区域的写入操作,其他对文件直接写入的操作将会被拒绝。
    MAP_LOCKED 将映射区域锁定住,这表示该区域不会被置换(swap)。

    参数fd:要映射到内存中的文件描述符。如果使用匿名内存映射时,即flags中设置了MAP_ANONYMOUS,fd设为-1。有些系统不支持匿名内存映射,则可以使用fopen打开/dev/zero文件,然后对该文件进行映射,可以同样达到匿名内存映射的效果。

    参数offset:文件映射的偏移量,通常设置为0,代表从文件最前方开始对应,offset必须是分页大小的整数倍。

    返回值:若映射成功则返回映射区的内存起始地址,否则返回MAP_FAILED(-1),错误原因存于errno 中。

    错误代码:

    EBADF 参数fd 不是有效的文件描述词
    EACCES 存取权限有误。如果是MAP_PRIVATE 情况下文件必须可读,使用MAP_SHARED则要有PROT_WRITE以及该文件要能写入。
    EINVAL 参数start、length 或offset有一个不合法。
    EAGAIN 文件被锁住,或是有太多内存被锁住。
    ENOMEM 内存不足。

    我们回顾一下mmap内存映射原理的三个阶段:

    1. 进程启动映射过程,并且在虚拟地址空间为映射创建虚拟映射区域;
    2. 调用内核空间的系统调用函数mmap(不同于用户空间函数),实现文件物理地址和进程虚拟的一一映射关系;
    3. 进程发起对这片映射空间的访问,引发缺页异常,实现文件内容到物理内存(主存)的拷贝。
    munmap()----删除内存映射
    #include <sys/mman.h>
    int munmap(void *addr, size_t len);
    
    mprotect()----设置虚拟内存区域的访问权限
    #include <sys/mman.h>
    int mprotect(void *addr, size_t len, int prot);
    
    //进程1
    #include <sys/mman.h>
    #include <sys/types.h>
    #include <fcntl.h>
    #include <string.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <errno.h>
    
    typedef struct 
    {
        /* data */
        char name[4];
        int age;
    }people;
    
    
    void main(int argc,char**argv)
    {
        int fd,i;
        people *p_map;
        char temp;
        fd=open(argv[1],O_CREAT|O_RDWR|O_TRUNC,00777);
    
        lseek(fd,sizeof(people)*5-1,SEEK_SET);
        write(fd,"",1);
    
        p_map=(people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
        if(p_map==(void*)-1)
        {
            fprintf(stderr,"mmap : %s \n",strerror(errno));
            return ;
        }
        close(fd);
    
        temp='A';
        for(i=0;i<10;i++)
        {
            (*(p_map+i)).name[1]='\0';
            memcpy((*(p_map+i)).name,&temp,1);
            (*(p_map+i)).age=30+i;
            temp=temp+1;
        }
    
        printf("Initialize.\n");
    
        sleep(15);
    
        munmap(p_map,sizeof(people)*10);
    
        printf("UMA OK.\n");
    
    }
    
    //进程2
    #include <sys/mman.h>
    #include <sys/types.h>
    #include <fcntl.h>
    #include <string.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <errno.h>
    
    typedef struct 
    {
        /* data */
        char name[4];
        int age;
    }people;
    
    void main(int argc,char**argv)
    {
        int fd,i;
        people *p_map;
    
        fd=open(argv[1],O_CREAT|O_RDWR,00777);
        p_map=(people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
        if(p_map==(void*)-1)
        {
            fprintf(stderr,"mmap : %s \n",strerror(errno));
            return ;
        }
    
        for(i=0;i<10;i++)
        {
            printf("name:%s age:%d\n",(*(p_map+i)).name,(*(p_map+i)).age);
        }
    
        munmap(p_map,sizeof(people)*10);   
    
    }
    
    //mprotect
    
    #include <unistd.h>
    #include <signal.h>
    #include <malloc.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <sys/mman.h>
    
    #define handle_error(msg) do{ perror(msg); exit(EXIT_FAILURE);}while(0)
    
    static char *buffer;
    
    static void handler(int sig,siginfo_t *si,void *unused)
    {
        printf("Get SIGSEGV at address : %p\n",si->si_addr);
        exit(EXIT_FAILURE);
    }
    
    int main(int argc,char *argv[])
    {
        int pagesize;
        struct sigaction sa;
    
        sa.sa_flags=SA_SIGINFO;
        sigemptyset(&sa.sa_mask);
        sa.sa_sigaction=handler;
    
        if(sigaction(SIGSEGV,&sa,NULL)==-1)
            handle_error("siaction");
    
        pagesize=sysconf(_SC_PAGE_SIZE);
        if(pagesize==-1)
            handle_error("sysconf");
    
        buffer=memalign(pagesize,4*pagesize);
        if(buffer==NULL)
            handle_error("memalign");
    
        printf("start of region : %p\n",buffer);
    
        if(mprotect(buffer+pagesize*2,pagesize,PROT_READ)==-1)
            handle_error("mprotect");
    
            for(char *p=buffer;;)
            *(p++)='A';
    
            printf("for completed.\n");
            exit(EXIT_SUCCESS);
    
        return 0;
    }
    
    展开全文
  • 内存映射文件原理探索.pdf
  • Java内存映射原理与实现

    千次阅读 2022-01-08 21:26:58
    Java虚拟机规范中定义了Java内存模型(Java Memory Model,JMM),用于屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的并发效果,JMM规范了Java虚拟机与计算机内存是如何协同...
  • mmap内存映射的实现过程,总的来说可以分为三个阶段: (一)进程启动映射过程,并在虚拟地址空间中为映射创建虚拟映射区域 1、进程在用户空间调用库函数mmap,原型:void*mmap(void*start,size_tlength,intprot,...
  • mongodb内存映射原理

    2019-07-13 10:03:10
    mongodb非常吃内存,为啥这么吃内存呢,mongodb使用的是内存映射存储引擎,即Memory Mapped Storage Engine,简称MMAP,MMAP可以把磁盘文件的一部分或全部内容直接映射内存,这样文件中的信息位置就会在内存中有...
  • iOS之深入解析文件内存映射MMAP

    万次阅读 热门讨论 2021-08-31 19:33:16
    一、常规文件操作 常规文件操作(read/write)有以下重要步骤: 进程发起读文件请求; 内核通过查找进程文件符表,定位到内核已打开文件集上的文件信息,从而找到此文件的 inode; inode 在 address_...
  • 一直都对内存映射文件这个概念很模糊,不知道它和虚拟内存有什么区别,而且映射这个词也很让人迷茫,今天终于搞清楚了。。。下面,我先解释一下我对映射这个词的理解,再区分一下几个容易混淆的概念,之后,什么是...
  • 1. 虚拟内存概要内容简介 1.1. mm_struct 和 vm_area_struct // mm_struct 和 vm_area_struct 的简要成员 struct mm_struct { unsigned long start_brk, brk, start_stack; } __attribute__((preserve_access_...
  • 共享内存原理文件映射

    千次阅读 2018-08-26 18:53:23
    共享内存原理文件映射 mmap()是将指定的文件利用虚拟内存技术映射到内存中去,在内存中建立页表项,但mmap()调用建立的页表项后面地址为空,只有使用到某个地址空间时才给此页面分配空间,要涉及到页面置换,...
  • 虚拟内存机制 转载处:http://blog.csdn.net/zp373860147/article/details/7815793; 这是我找到的关于虚拟内存和分页机制的我个人比较容易理解的一篇文章。  虚拟存储器的思想是程序、数据和堆栈的大小都...
  • mmap内存映射原理

    2019-07-10 14:02:27
     mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。  特点:实现这样的映射关系后,进程就可以采用指针的方式...
  • 1. 前言 最近研究Java中内存映射I/O。...本文章将从操作系统I/O调用原理讲解为什么内存映射文件MappedByteBuffer相比较Java I/O性能极高。话不多说,我们开始学习吧。 2. 浅谈Java I/O InputStr...
  • linux内存映射mmap原理分析

    万次阅读 2018-05-29 10:41:44
    linux内存映射mmap原理分析转自:http://blog.csdn.net/yusiguyuan/article/details/23388771 内存映射,简而言之就是将用户空间的一段内存区域映射到内核空间,映射成功后,用户对这段内存区域的修改可以直接反映...
  • 认识文件 The following figure illustrates how a File System works: Every modern Operating System (OS) has a component called aFile System. That component is part...
  • 原理介绍 在Windows平台中,常见的进程间通信机制包括管道、socket、WM_COPYDATA、邮槽等...(1)创建一个文件映射内核对象(file-mapping kernel object)并指定系统文件大小以及访问方式。 (2)把文件映射对象的...
  • Linux内存映射实现

    千次阅读 2022-01-16 17:09:42
    1、内存管理架构图 分为用户空间,内核空间,硬件三个层面: ...虚拟内存管理,负责把用户地址映射成虚拟地址,从进程的虚拟地址空间分配虚拟页 sys_brk()用来扩大缩小堆, sys_mmap()用来内存
  • 应用内存映射文件技术将分割后的大场景流域模型(包括DEM地形、地形影像、地物模型、地物模型影像、元数据)在内存储器中建立了多进程共享的内存地址空间。在此基础上首先探讨了应用内存映射文件技术所用到的函数...
  • NIO效率高的原理之零拷贝与直接内存映射

    万次阅读 多人点赞 2019-07-31 08:00:00
    首先澄清,零拷贝与内存直接映射并不是Java中独有的概念,并且这两个技术并不是等价的。 零拷贝 零拷贝是指避免在用户态(User-space) 与内核态(Kernel-space) 之间来回拷贝数据的技术。 传统IO 传统IO读取数据并...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 161,113
精华内容 64,445
关键字:

内存文件映射的原理