虚拟地址空间相关知识网络(段页存储 -- MMU -- 虚拟地址 -- 内核区 -- 用户区)

Posted 狱典司

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了虚拟地址空间相关知识网络(段页存储 -- MMU -- 虚拟地址 -- 内核区 -- 用户区)相关的知识,希望对你有一定的参考价值。

知识联系:
段页式存储 ----> 逻辑地址到物理地址映射(MMU) ----> 进程地址空间(用户地址空间和PCB)-----> 缺页中断和缺页请求

在Linux中,每个进程都有属于自己的进程控制块(PCB)和地址空间(Addr Space),并且都有一个与之对应的页表,负责将进程的虚拟地址与物理地址进行映射,通过内存管理单元(MMU)进行管理。

虚拟地址空间

  1. 虚拟地址空间对应一段连续的内存地址,起始为逻辑上的0(不是物理内存的0地址);
  2. 用来加载程序数据(数据可能被加载到物理内存上,空间不够就加载到虚拟内存中)。

虚拟地址空间的大小也由操作系统决定,32位的操作系统虚拟地址空间的大小为 2^32 字节,也就是 4G,64 位的操作系统虚拟地址空间大小为 2^64 字节。

虚拟地址空间与进程用户地址空间的联系:

当我们运行磁盘上一个可执行程序, 就会得到一个进程,内核会给每一个运行的进程创建一块属于自己的虚拟地址空间,并将应用程序数据装载到虚拟地址空间对应的地址上。

MMU

进程在运行过程中,程序内部所有的指令都是通过 CPU 处理完成的,CPU 只进行数据运算并不具备数据存储的能力,其处理的数据都加载自物理内存

问题:进程中的数据是如何进出入到物理内存中

进程中的数据 通过 CPU 中的**内存管理单元 MMU(Memory Management Unit)**从进程的虚拟地址空间中映射到物理内存上。

操作系统将进程数据通过CPU的MMU从虚拟地址空间映射到物理内存上。

为什么操作系统不直接将数据加载到物理内存中?

  1. 每个进程的地址不隔离,有安全风险;

    恶意程序可以通过内存寻址随意修改别的进程对应的内存数据。

  2. 内存效率低;

    如果直接使用物理内存的话,一个进程对应的内存块就是作为一个整体操作的,如果出现物理内存不够用的时候,我们一般的办法是将不常用的进程拷贝到磁盘的交换分区(虚拟内存)中,以便腾出内存,因此就需要将整个进程一起拷走,如果数据量大,在内存和磁盘之间拷贝时间就会很长,效率低下。

  3. 进程中数据的地址不确定,每次都会发生变化。

    由于物理内存的使用情况一直在动态的变化,我们无法确定内存现在使用到哪里了,如果直接将程序数据加载到物理内存,内存中每次存储数据的起始地址都是不一样的,这样数据的加载都需要使用相对地址,加载效率低(静态库是使用绝对地址加载的)。

虚拟地址空间就是一个中间层,相当于在程序和物理内存之间设置了一个屏障,将二者隔离开来。程序中访问的内存地址不再是实际的物理内存地址,而是一个虚拟地址,然后由操作系统(CPU的MMU)将这个虚拟地址映射到适当的物理内存地址上

这样,只要操作系统处理好虚拟地址到物理内存地址的映射,就可以保证不同的程序最终访问的内存地址位于不同的区域,彼此没有重叠,就可以达到内存地址空间隔离的效果。

这就是进程地址空间相对隔离的底层原因。

从操作系统层级上看,虚拟地址空间主要分为两个部分内核区和用户区。

一、内核区

  1. 内核空间为内核保留,不允许应用程序读写该区域的内容或直接调用内核代码定义的函数
  2. 内核总是驻留在内存中,是操作系统的一部分。
  3. 系统中所有进程对应的虚拟地址空间的内核区都会映射到同一块物理内存上(系统内核只有一个

二、用户区

每个进程的虚拟地址空间都是从 0 地址开始的,我们在程序中打印的变量地址也其在虚拟地址空间中的地址,程序是无法直接访问物理内存的。虚拟地址空间中用户区地址范围是 0~3G(以 32 位系统的虚拟地址空间为例),里边分为多个区块。

各分区由低地址到高地址依次是:

  1. 保留区: 位于虚拟地址空间的最底部,未赋予物理地址。任何对它的引用都是非法的,程序中的空指针(NULL)指向的就是这块内存地址

  2. .text段: 代码段也称正文段或文本段,通常用于存放程序的执行代码 (即 CPU 执行的机器指令,二进制),代码段一般情况下是只读的,这是对执行代码的一种保护机制。

  3. .data段: 数据段通常用于存放程序中已初始化且初值不为 0 的全局变量和静态变量。数据段属于静态内存分配 (静态存储区),可读可写。

  4. .bss段: 未初始化以及初始为 0 的全局变量和静态变量,操作系统会将这些未初始化变量初始化为 0

  5. 堆(heap):用于存放进程运行时动态分配的内存

    • 堆中内容是匿名的,不能按名字直接访问,只能通过指针间接访问。
    • 堆向高地址扩展 (即 “向上生长”),是不连续的内存区域。这是由于系统用链表来存储空闲内存地址,自然不连续,而链表从低地址向高地址遍历
  6. 内存映射区(mmap):作为内存映射区加载磁盘文件,或者加载程序运作过程中需要调用的动态库

  7. 栈(stack): 存储函数内部声明的非静态局部变量,函数参数,函数返回地址等信息,栈内存由编译器自动分配释放和堆相反地址 “向下生长”,分配的内存是连续的。

  8. 命令行参数:存储进程执行的时候传递给 main() 函数的参数,argc,argv []

  9. 环境变量: 存储和进程相关的环境变量,比如:工作路径进程所有者等信息

以上是关于虚拟地址空间相关知识网络(段页存储 -- MMU -- 虚拟地址 -- 内核区 -- 用户区)的主要内容,如果未能解决你的问题,请参考以下文章

MMU内存管理单元相关知识点总结

存储系统-------虚拟存储

存储管理-段页式管理

linux段页式内存管理技术

操作系统 内存管理单元MMU TLB

操作系统 内存管理单元MMU TLB