linux的内存机制

  1. 物理内存和虚拟内存,虚拟地址和物理地址。
    • 计算机存储一般包含高速缓存(cpu寄存器),内存储,外存储,而外存储又包含计算机内部磁盘和外接存储磁盘等。
    • 内存:计算机的内存条组成了计算机的内存,容量包含早期的256M,512M,4G,当前的8G,16G等,内存介于高速缓存和外存中间,cpu寄存器等高速缓存访问速度快但是空间小,对于大的程序不能够完全加载,内存速度比cpu稍慢但是空间比cpu大的多,主要作用是计算机运行时为os和程序提供临时存储。内存分为物理内存和虚拟内存。
    • 物理内存,在具体事物上,物理内存就是内存,就是安装内存条获取的内存。
    • 虚拟内存:虚拟内存是操作系统为了扩展物理内存,解决单独使用物理内存存在的问题而提出的一种内存管理技术,虚拟内存是使得程序认为分配得到一个连续可用的内存空间(并不是真正分配而通常是将一块空间分割成多个物理碎片。还有部分存储在外存储上,在使用的时候进行数据交换)。


      虚拟内存与物理内存的关系
    • 物理地址/虚拟地址
      • 计算机存储每一个数据都存在一个地址,程序通过这个地址找到数据进行计算,物理内存上的数据对应的地址被称为物理地址,虚拟内存上的地址被称为虚拟地址。
      • 数据的最终存储还是在物理内存上,但是在虚拟内存上是虚拟地址和物理地址的映射,物理内存上是物理地址和数据。
    • 早期仅适用物理内存的缺点/虚拟内存的优势
      • 早期计算机没有虚拟内存一说,仅有物理内存,程序寻址都是直接使用物理地址,寻址范围是有限的,寻址范围取决于cpu的地址线条数,在32位平台下是2^32即4G,并且是固定的,即每一个进程程序都会分配4G内存。仅使用物理内存会出现下面问题:
        • 物理内存有限,以当前比较大的内存条16G来说也仅仅能跑4个app,app多并发的时候没有分配到内存的程序只能等待,等待分配内存的程序执行完成,才能装入内存执行。
        • 指令直接访问物理内存,进程间就可以直接修改数据,甚至程序可以修改系统进程的数据,进程间数据安全得不到保证。
    • 为了解决上面问题提出了虚拟内存的概念,虚拟内存还是分配给每个运行的进程4G内存,但是物理内存并没有真正的分配这么大,而是当前需要多少内存分配多少,还有就是分配的虚拟内存 程序进程认为是连续不间断的内存块 而真正的并不是 通常被分割成多个物理内存碎块,甚至不在内存上而是在外存储上,在使用的时候才会数据交换。
      • 虚拟内存怎么扩展物理内存?
        • 虚拟内存是以页为基本单位 存储的是数据的虚拟地址和物理地址映射并不是直接存储数据。
        • 虚拟内存号称进程分配4G空间但是并未真正的分配物理空间 而是根据需求分配具体的物理空间,后续会增会减。
        • 虚拟内存号称分配连续不间断内存分配而真正是隔断的物理内存碎片,提升了物理内存的使用率。
        • 虚拟内存会将一些数据存储到外存储,使用的时候再加载到内存中 扩展了内存的存储量。
        • 程序很多通用库,最简单就是系统提供工具库,这样系统单独分配一块物理内存存储,其他程序都建立到系统库的虚拟内存映射即可。
      • 虚拟内存不直接操作物理地址,且给每个程序分配固定的内存空间,内存空间不能够直接操作,提升了进程数据的安全性,不过通常系统都提供了进程间通信方式,这个后续解释,此处不提。
  2. 需要了解的linux的内存术语:内核空间,用户空间
    • linux针对单个进程分配的的虚拟内存可以分为内核空间和用户空间,简单说运行系统服务的内存空间被称为内核空间,运行程序自己数据的内存空间被称为用户空间。
    • 内核空间:内核本质上是一个软件,具有最高的权限,控制计算机的资源并提供上层程序的运行环境,存储内核程序的空间被称为内核空间,内核程序通常是硬件驱动程序,中断控制程序,内核调度程序,内存管理程序。
    • 用户空间: 运行用户自己程序,存储程序数据的空间被称为用户空间。
    • linux针对内核空间和用户空间有着明确的权限控制,用户空间不能够直接访问内核空间,但是提供了进程通信方式使得用户空间可以使用内核空间提供的服务及其数据。
    • linux内核程序各进程复用,存储在一块独立物理内存上,程序启动系统进程会给程序分配用户空间,用户空间大小是4G 具体的分配如下(此处容易迷糊的地方,各进程复用内核程序但是又拥有自己的内核空间,主要是要理解各进程复用的是内核程序及其对应的物理内存,而内核空间和用户空间都是针对虚拟内存的,内核空间是当前程序和系统程序的地址映射页表,用户空间是自己程序的地址映射页表,即内核复用说的对象是物理地址,两个空间对应的是虚拟地址):


      内核空间和用户空间存储详情表
    • linux进程分配虚拟内存空间是4G,0到3G是用户空间,3G到4G是内核空间。
    • 程序单个进程要占用一定数量的内存,用来存放磁盘载入的程序代码,或者存放取自用户输入的数据等,但是进程对于这些内存的管理方式不同,有的是静态分配和统一回收(java 静态变量和静态方法),有的是按需动态分配和回收(java 变量和方法)
    • 进程数据存储通常包括五部分:代码段,数据段,bss,堆,栈,下面详细叙述这五种数据(存储在用户空间上):
      • 程序代码段:存放可执行程序文件的操作指令,也就是说是可执行程序在内存中的镜像(代码备份),代码段需要防止运行的时候被修改,所以代码段仅允许读取不允许复写。
      • 程序数据段:存储可执行程序中已经初始化的静态全局变量,即程序静态分配变量和全局变量。
      • Bss数据:可执行程序中未被初始化的全局变量,存储在bss的变量都被置为0.
      • 堆:存放进程运行的时候动态分配的内存段,大小不固定,可动态扩展和回收,malloc等函数动态申请内存,则会动态添加到堆内存上,利用free等函数回收内存,被释放的内存从堆内存上剔除。
      • 栈:存放进程运行时动态创建的内存段的指针或者动态局部变量,函数参数,函数返回值也都会被存储在栈中,由此栈可以被看做是寄存,交换临时数据的内存区。
    • 内核空间通常分为:4组内存区,第一组是物理内存线性映射(有序对应)大小标准的是896M,第二组是8M的high_memory(高端内存),第三组是以4096 b 为单位的内存段 最后是128K的内存区。
    • 用户空间的虚拟地址结构是Vm_area_structs,内核空间的虚拟地址结构是vm_structs.
  3. linux如何分配,回收,管理虚拟内存的
    • 用户分配虚拟内存图
    • linux采用的是虚拟内存管理技术,针对每一个进程都分配4G连续虚拟内存(进程这样认为的)空间,分为用户空间(0到3G)和内核空间(3G到4G),linux对内存的管理是通过虚拟地址到物理地址映射来管理的(逻辑地址-线性地址-物理地址,linux针对每一个进程分配的内存空间都是0到4G,所以逻辑地址等同于线性地址),具体的分配,回收,管理区分来说。
      • 用户空间由上可知分为五部分,五部分内存管理方式也不同,针对代码段和数据段和bss段则是进程创建后将它们的信息(基地址,长度等)更新到进程控制块,当cpu执行的时候引起缺页中断,os再将实际内容copy到物理内存中去。而堆和栈则是动态分配,是进程创建后先根据当前内存的申请分配对应内存,后续逻辑动态申请分配和回收对应内存,执行流程上和代码段等类似,先将起始地址更新到进程控制块中,后续执行过程中才会分配真正的物理内存。
      • 内核空间和用户空间这不同,内核进程在计算机启动已创建并分配了单独物理内存中,进程创建的时候会在3g到4g虚拟内存创建进程到内核的地址映射,此映射各个进程相同。进程销毁的时候回收(待定,个人理解 后续待验证)。
    • 用户空间和内核空间各个进程特性如下:
      • 进程用户空间不能够直接访问内核空间,只有通过系统调用才能访问内核空间
      • 进程 用户空间随着进程变化而变化且各个进程的用户空间各不相同 ,内核空间由内核负责映射,不会发生变化
      • 各个进程的用户空间独立,不能互相访问。
    • do_mmap函数/do_ummap函数
      • 用户空间内存的分配最终皆有函数do_mmap分配,fork(进程创建)、execve(载入程序)、mmap(映射文件)、malloc(动态分配内存)最终都会到do_mmap,且这个时候分配的是虚拟内存并不是真正的物理内存。
      • 释放内存则是通过do_ummap函数实现。
    • 进程所能操作的都是虚拟地址,需要内存的时候分配的也都是虚拟内存,只有当进程正式访问虚拟地址的时候才会发生缺页异常 正式进入创建页面的流程。
    • linux是通过页表来管理虚拟地址和物理地址映射的。
  4. linux的页表管理:
    • 图片可以继续参考上面的图片,页表的创建包括用户空间的页面创建及其内核空间的页表创建。
    • 用户空间的页表创建及其维护:
      • Linux内核管理物理内存是通过分页机制实现的,它将整个内存划分成无数个4k(在i386体系结构中)大小的页,从而分配和回收内存的基本单位便是内存页了。利用分页管理有助于灵活分配内存地址,因为分配时不必要求必须有大块的连续内存,系统可以东一页、西一页的凑出所需要的内存供进程使用。虽然如此,但是实际上系统使用内存时还是倾向于分配连续的内存块,因为分配连续内存时,页表不需要更改,因此能降低TLB的刷新率(频繁刷新会在很大程度上降低访问速度)。
      • linux的空闲页面的组织和管理使用伙伴算法,空闲页面分配回收都遵循伙伴算法,伙伴算法的原理是:把所有的空闲页框分组为 11 块链表,每一块链表分别包含大小为1,2,4,8,16,32,64,128,256,512 和 1024 个连续的页框。对1024 个页框的最大请求对应着 4MB 大小的连续RAM 块。
        • 分配内存:以256为例,查找链表 是否存在256个页框,若存在直接分配,若不存在 向上查找,512的 若存在 256分配,另外256查到对应256的链表上,若不存在 继续向上查找 直到 1024 还不存在 则报异常。
        • 回收内存:以256为例,若存在256则直接插到256链表上,和分配不同的是插入后会有一个合并的操作,校验插入页面内存地址附近是否存在等大的页框若存在 尝试合并后插入到更大的链表上。
      • 当每个进程创建的时候,内核会为进程分配4G的虚拟内存,当进程还没有开始运行时,这只是一个内存布局。实际上并不立即就把虚拟内存对应位置的程序数据和代码(比如.text .data段)拷贝到物理内存中,只是建立好虚拟内存和磁盘文件之间的映射就好(叫做存储器映射)。这个时候数据和代码还是在磁盘上的。当运行到对应的程序时,进程去寻找页表,发现页表中地址没有存放在物理内存上,而是在磁盘上,于是发生缺页异常,于是将磁盘上的数据拷贝到物理内存中。
      • 另外在进程运行过程中,要通过malloc来动态分配内存时,也只是分配了虚拟内存,即为这块虚拟内存对应的页表项做相应设置,当进程真正访问到此数据时,才引发缺页异常。
      • 可以认为虚拟空间都被映射到了磁盘空间中(事实上也是按需要映射到磁盘空间上,通过mmap,mmap是用来建立虚拟空间和磁盘空间的映射关系的)
    • 内核空间的内存分配
      • 上面页表方式分配
      • slab内核内存分配
      • Vmalloc 非连续内存分配
    • 内部分片和外部分片问题及其解决
      • 内部分片:针对小的内存申请 os都是分配大的内存块造成内存浪费 slab可以减少内部分片 Vmalloc非连续的内存分配可以解决内部分片问题
      • 外部分片:系统有足够的内存,但是内存都是碎片 不能分配造成内存浪费 伙伴关系解决内部分片问题
  5. linux的进程通信方式:
    • 管道
    • 共享内存
    • 文件
    • socket通信

参考文章:
Linux系统内存管理
Linux系统内存管理-最透彻一篇
虚拟内存和物理内存关系与区别
linux内核空间和用户空间
linux进程最大内存分配
linux 伙伴算法
linux进程加载流程
windows 内存机制管理

你可能感兴趣的:(linux的内存机制)