【Linux内核】内存映射原理

物理地址空间

  1. 物理地址是处理器在总线上能看到的地址,使用RISC(Reduced Instruction Set Computing精简指令集)的处理器通常只实现一个物理地址空间,外围设备和物理内存使用统一的物理空间,

    有些架构的处理器把分配给外围设备的物理地址称为设备内存

    处理器通过外围设备控制器里面的寄存器来访问外围设备,寄存器分为控制寄存器,状态寄存器和数据寄存器

    外围设备的寄存器通常被常备连续的编址,处理器对外围设备寄存器的编址方式分为两种:I/O映射方式,内存映射方式

    • IO映射方式:x86处理器专门为外围设备单独提供一个空间,通过单独的指令(如in out)来访问这个空间的地址
    • 内存映射方式:外围设备和物理内存使用同一个物理空间,处理器以相同方式访问物理内存和外围设备,那么应用如何访问外围设备呢? 操作系统提供单独的函数将外围设备对应的地址映射到应用的虚拟内存中,从而使应用能访问设备

    ARM64架构分为两种内存类型:

    • 正常内存(Normal Memory) :包括物理内存和只读存在器(ROM) ;

      对于正常内存来讲可以设置共享属性,包括缓冲区的共享属性,共享属性分为不可共享,内部共享和外部共享.不可共享:制备处理器一个核心使用,内部共享:可以被多个核使用,外部共享:可以被DMI(DMI是指Direct Media InterfaceI(直接媒体接口))等共享1

    • 设备内存(Device Memory)∶指分配给外围设备寄存器的物理地址区域

      设备内存的共享属性总是外部共享

内存映射原理

​ 创建内存映射时,在进程的用户虚拟地址空间中分配一个虚拟内存区域,内核采用一个延迟物理内存分配策略,在进程第一次访问虚拟页的时候产生缺页异常2

​ 如果是文件映射,那么分配物理页把文件指定的区域数据读到物理页当中,然后把应用的虚拟页表映射到物理页,

​ 如果是匿名映射,就分配物理页,让后把虚拟页映射到物理页

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

  • 文件映射:文件支持内存映射,把文件的一段区域映射进程的虚拟空间,文件源式存储设备上的文件

    两个进程可以使用共享的文件映射实现共享内存

  • 匿名映射3:没有文件支持的映射,把物理内存映射到进程里的虚拟空间,无数据源

    匿名映射通常是私有映射,只可能出现在父进程和子进程之间

在进程的虚拟地址空间中,代码段和数据段是私有文件映射

未初始化的数据段,堆栈是私有的匿名映射

线程启动映射过程,并且在虚拟地址空间中为内存映射创建一个映射区,先在用户空间调用mmap函数,并且在内存虚拟空间中找到一段连续空闲的符合要求的虚拟地址,对这个区域初始化,并插入进程虚拟空间地址的链表,让后再内核中系统调用,实现文件的物理地址和进程虚拟地址之间一一对应的关系,进程开始查询文件内容,这是虽然进程虚拟内存与文件物理地址已经建立映射关系,但由于文件相关区域还没有加载到物理内存中,这是就会引发缺页中断,请求将磁盘内容调入内存当中,先在进程缓存空间swapcathe4中进行查找,如果没有找到,就通过nopage()把缺页从磁盘调入内存,如果你对内存进行写入改变内存内容,一段时间后,系统就会将改变的脏页写入硬盘,也可以使用函数强制及时同步.

数据结构

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 addresswithin 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;/** 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. */unsigned long vm_flags;        /* Flags, see 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);/* notification that a previously read-only page is about to become* writable, if an error is returned it will cause a SIGBUS */int (*page_mkwrite)(struct vm_area_struct *vma, struct vm_fault *vmf);/* same as page_mkwrite when using VM_PFNMAP|VM_MIXEDMAP */int (*pfn_mkwrite)(struct vm_area_struct *vma, struct vm_fault *vmf);
}

系统调用

​ 应用程序通常使用C标准库提供的函数malloc()申请内存,glibc库的内存分配器ptmalloc使用brk或mmap向内核以页为单位申请虚拟内存然后把页划分为小块共应用程序使用

​ 应用程序可以使用mmap向内核申请虚拟内存

1、mmap()----创建内存映射

#include <sys/mman.h> void *mmap(void *addr,size_t length,int prot,int flags,int fd,off_t offset);

系统调用mmap():进程创建匿名的内存映射,把内存的物理页映射到进程的虚拟地址空间。进程把文件映射到进程的虚拟地址空间,可以像访问内存一样访问文件,不需要调用系统调用read()/write()访问文件,从而避免用户模式和内核模式之间的切换,提高读写文件速度。 两个进程针对同一个文件创建共享的内存映射,实现共享内存。

2、munmap()----删除内存映射

#include <sys/mman.h> int munmap(void *addr, size_t len);

3、mprotect()----设置虚拟内存区域的访问权限

#include <sys/mman.h> int mprotect(void *addr, size_t len, int prot);

1、进程启动映射过程,并且在虚拟地址空间中为映射创建虚拟映射区域;
2、调用内核空间的系统调用函数mmap(不同于用户空间函数),实现文件物理地址和进程虚拟的一一映射关系;
3、进程发起对这片映射空间的访问,引发缺页异常,实现文件内容到物理内存(主存)的拷贝。

上代码:

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.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++){temp = temp + 1;(*(p_map + i)).name[1] = '\0';memcpy((*(p_map + i)).name, &temp, 1);(*(p_map + i)).age = 30 + i;}printf("Initialize.\n");sleep(15);munmap(p_map, sizeof(people) * 10);printf("UMA OK.\n");
}
#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);   }

**我所有的学习笔记都在Github仓库里:https://github.com/fanxing-6/CPP-learning-notes **

,如果访问Github有问题也可以访问Gitee:CPP-learning-notes: C++学习笔记 (gitee.com)

本人能力有限,笔记难免有疏漏,如果有错误,欢迎各位关注公众号与我交流,一定会及时回复


  1. DMI是Intel(英特尔)公司开发用于连接主板南北桥的总线,取代了以前的Hub-Link总线。DMI采用点对点的连接方式,时钟频率为100MHz,由于它是基于PCI-Express总线,因此具有PCI-E总线的优势。DMI实现了上行与下行各1GB/s的数据传输率,总带宽达到2GB/s,这个高速接口集成了高级优先服务,允许并发通讯和真正的同步传输能力。它的基本功能对于软件是完全透明的,因此早期的软件也可以正常操作。 ↩︎

  2. 缺页异常,页缺失Page fault,指的是硬错误、硬中断、分页错误、寻页缺失、缺页中断、页故障等)指的是当软件试图访问已映射在虚拟地址空间中,但是目前并未被加载在物理内存中的一个分页时,由中央处理器的内存管理单元所发出的中断。通常情况下,用于处理此中断的程序是操作系统的一部分。如果操作系统判断此次访问是有效的,那么操作系统会尝试将相关的分页从硬盘上的虚拟内存文件中调入内存。而如果访问是不被允许的,那么操作系统通常会结束相关的进程。虽然其名为“页缺失”错误,但实际上这并不一定是一种错误。而且这一机制对于利用虚拟内存来增加程序可用内存空间的操作系统(比如Microsoft Windows和各种类 Unix 系统)中都是常见且有必要的。 ↩︎

  3. mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上,即完成了对文件的操作而不必再调用read,write等系统调用函数。相反,内核空间对这段区域的修改也直接反映用户空间,从而可以实现不同进程间的文件共享。进程在用户空间调用库函数mmap,原型:void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);来实现内存映射。从原型可知,存在一个参数为fd,根据fd,存在一种情况叫匿名映射,所谓匿名映射,表示不存在fd这么个真实的文件。实现匿名映射的方式主要有以下两种: 1、BSD 提供匿名映射的办法是fd =-1,同时 flag 指定为MAP_SHARE|MAP_ANON。 ptr = mmap(NULL,sizeof(int),PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON,-1,0);2、SVR4 提供匿名映射的办法是 open /dev/zero设备文件,把返回的文件描述符,作为mmap的fd参数。 fd = open("/dev/zero",O_RDWR); /dev/zero 是一个特殊的文件,当你读它的时候,它会提供无限的空字符(NULL, ASCII NUL, 0x00) 一个作用是用它作为源,产生一个特定大小的空白文件。匿名内存映射适用于具有亲属关系的进程之间;由于父子进程之间的这种特殊的父子关系,在父进程中先调用mmap(),然后调用fork(),那么,在调用fork() 之后,子进程继承了父进程的所有资源,当然也包括匿名映射后的地址空间和mmap()返回的地址,这样父子进程就可以通过映射区域进行通信了;这里不是一般的继承关系,一般来说,子进程单独维护从父进程继承下来的一些变量,而mmap()返回的地址却是由父子进程共同维护的;对于具有亲属关系的进程之间实现共享内存的最好方式应该是采用匿名映射的方式。此时,不必指定具体的条件,只要设置相应的标志即可。 ↩︎

【Linux内核】内存映射原理相关推荐

  1. linux内核内存映射实验报告,动手实践-Linux内存映射基础(上)

    Linux内核分析与应用 西安邮电大学(陈莉君) 在庞大的Linux内核源代码学习中,如何抓住主要线索和思路,如何让所学能够切实地应用起来,本课程主讲以自己20年来对Linux内核的研究和教学为基础, ...

  2. 【Linux 内核 内存管理】内存映射原理 ① ( 物理地址空间 | 外围设备寄存器 | 外围设备寄存器的物理地址 映射到 虚拟地址空间 )

    文章目录 一.物理地址空间 二.外围设备寄存器 三.外围设备寄存器物理地址 映射到 虚拟地址空间 一.物理地址空间 " 物理地址空间 " 是 CPU 处理器 在 " 总线 ...

  3. Linux内核内存管理(2):固定映射地址(fixmap)和输入输出重映射(ioremap)

    Linux内核内存管理 固定映射地址(fixmap)和输入输出重映射(ioremap) rtoax 2021年3月 在英文原文基础上,针对中文译文增加5.10.13内核源码相关内容. Print ke ...

  4. 【Linux 内核 内存管理】内存映射相关数据结构 ③ ( vm_area_struct 结构体成员分析 | shared 成员 | anon_vma_chain 成员 | anon_vma 成员 )

    文章目录 一.vm_area_struct 结构体成员分析 1.shared 成员 2.anon_vma_chain 成员 3.anon_vma 成员 二.vm_area_struct 结构体完整源码 ...

  5. 【Linux 内核 内存管理】分区伙伴分配器 ② ( free_area 空闲区域结构体源码 | 分配标志位 | GFP_ZONE_TABLE 标志位区域类型映射表 |分配标志位对应的内存区域类型 )

    文章目录 一.free_area 空闲区域结构体源码分析 二.分配标志位 三.GFP_ZONE_TABLE 标志位区域类型映射表 四.分配标志位对应的内存区域类型 一.free_area 空闲区域结构 ...

  6. 【Linux 内核 内存管理】内存管理架构 ② ( 用户空间内存管理 | malloc | ptmalloc | 内核空间内存管理 | sys_brk | sys_mmap | sys_munmap)

    文章目录 一.用户空间内存管理 ( malloc / free / ptmalloc / jemalloc / tcmalloc ) 二.内核空间内存管理 1.内核内存管理系统调用 ( sys_brk ...

  7. Linux内核内存管理(1):内存块 - memblock

    Linux内核内存管理 内存块 - memblock rtoax 2021年3月 在英文原文基础上,针对中文译文增加5.10.13内核源码相关内容. 1. 简介 内存管理是操作系统内核中最复杂的部分之 ...

  8. 一文搞懂内存映射原理及使用方法

    a. 内存映射原理 内存映射即在进程的虚拟地址空间中创建一个映射,分为两种: 文件映射:文件支持的内存映射,把文件的一个区间映射到进程的虚拟地址空间,数据源是存储设备上的文件. 匿名映射:没有文件支持 ...

  9. Linux mmap内存映射

    将最近网上搜索的资料统一整理下,方便后续复查. 一.什么是mmap mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一 ...

最新文章

  1. 特征工程之时间特征、自然语言特征、图像图像特征、数据分布不平衡
  2. 初创公司谁来当你们的运维
  3. python交作业的格式_python作业4
  4. 游戏开发攻略—黑杰克扑克牌
  5. jsp页面定义的map
  6. [译]课程 9: 作业存储
  7. 直播预告 | 中国工程院院刊:信息与电子工程领域青年学术前沿论坛
  8. 新乡台达服务器驱动器维修,台达DELTA伺服驱动器维修
  9. java程序员遇到的问题_JAVA程序员最常遇见的10个异常
  10. 用hundred造句子_八个有趣的开学破冰游戏,线上线下都能用
  11. 计算机视觉基础——对极几何(Epipolar Geometry)
  12. gateway中的局部过滤器_vue 过滤器
  13. 学了python能干啥举例-django能干什么
  14. Spring-day02
  15. jmeter录制脚本的两种方式
  16. OpenJudge NOI题库 入门 116题 (二)
  17. TinaFace: Strong but Simple Baseline for Face Detection论文笔记
  18. gephi和python_介绍用Gephi进行数据可视化
  19. 慕课《如何写好科研论文》Quiz汇总
  20. 你不可不知的《哈利波特》秘密(五)

热门文章

  1. java 前置通知_spring aop中的前置通知
  2. vue render函数_Vue原理解析(一):Vue到底是什么?
  3. crc算法java_c语言的crc16算法转java
  4. php创建无限级树型菜单以及三级联动菜单
  5. flask中 app.run(host='0.0.0.0', port=5000, debug=False) 不能用外网ip访问的解决办法
  6. 几种常用的清除浮动方法(一)
  7. linux centos7.2 nodeJs全局安装
  8. React开发(264):react使用国际化
  9. React开发(130):ant design学习指南之form中的新城多个表单
  10. [html] websocket和socket有什么区别?