虚拟地址空间

虚拟地址空间

1.1 概述

虚拟地址空间是指操作系统为每个运行中的进程分配的抽象地址空间。这个地址空间使得每个进程都觉得它拥有一个独立的、连续的内存区域,而实际上这个内存区域可能分布在物理内存的不同位置,甚至可能被存储在磁盘上。

在虚拟地址空间中,进程访问的是虚拟地址,而不是实际的物理地址操作系统(MMU, 内存管理单元, Memory Management Unit)负责将虚拟地址映射到物理地址,这个过程称为地址转换。通过虚拟地址空间,操作系统可以为每个进程提供独立的、隔离的内存空间,从而增加了系统的稳定性和安全性。

虚拟地址空间_第1张图片

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

小结:

虚拟地址空间

  • 它可以用来加载程序数据(数据可能被加载到物理内存上,空间不够就加载到虚拟内存中)
  • 它对应着一段连续的内存地址,起始位置为 0。
  • 之所以说虚拟是因为这个起始的0地址是被虚拟出来的, 不是物理内存的 0 地址。
1.2 存在的意义
  1. 更大的地址空间: 虚拟内存使得每个进程能够拥有一个更大的地址空间,远远超过物理内存的大小。这允许程序处理比实际可用物理内存更大的数据集,提高了系统的灵活性和性能。
  2. 隔离和保护: 虚拟内存提供了进程间的隔离,使得每个进程认为它在独占的内存空间中运行,而不受其他进程的影响。同时,虚拟内存也提供了对内存的保护机制,防止一个进程无意或恶意地访问另一个进程的内存空间。
  3. 简化内存管理: 使用虚拟内存,操作系统可以更轻松地管理内存。**进程的虚拟地址空间可以被映射到物理内存,也可以在需要时被置换到磁盘上。**这样,操作系统可以更灵活地分配和回收内存,提高了内存的利用率。
  4. 提高程序的执行效率: 虚拟内存允许系统将进程的部分代码和数据加载到物理内存中,而不是全部加载。这样,只有在需要时才会将数据从磁盘加载到内存,从而减少了启动时间和内存占用,提高了程序的执行效率。
  5. 支持共享和动态加载: 虚拟内存使得多个进程可以共享相同的代码段,这在多任务操作系统中很重要。此外,虚拟内存还支持动态加载,允许程序在运行时加载和卸载代码和数据,提供更灵活的应用程序设计和执行环境。

小结: **虚拟地址空间就是一个中间层,相当于在程序和物理内存之间设置了一个屏障,将二者隔离开来。程序中访问的内存地址不再是实际的物理内存地址,而是一个虚拟地址,然后由操作系统将这个虚拟地址映射到适当的物理内存地址上。**这样,只要操作系统处理好虚拟地址到物理内存地址的映射,就可以保证不同的程序最终访问的内存地址位于不同的区域,彼此没有重叠,就可以达到内存地址空间隔离的效果。

1.3 分区
  • 内核区

    • 内核空间为内核保留,不允许应用程序读写该区域的内容或直接调用内核代码定义的函数。

    • 内核总是驻留在内存中,是操作系统的一部分。

    • 系统中所有进程对应的虚拟地址空间的内核区都会映射到同一块物理内存上(系统内核只有一个)。

  • 用户区: 存储用户程序运行中用到的各种数据。

虚拟地址空间_第2张图片

  • 用户区(0G - 3G)进一步划分

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

  • 保留区: 位于虚拟地址空间的最底部,**未赋予物理地址,暂时不用于特定的目的,以备将来的需求。**任何对它的引用都是非法的,程序中的空指针(NULL)指向的就是这块内存地址。
  • .text段: 代码段也称正文段或文本段,通常用于存放程序的执行代码(即CPU执行的机器指令),代码段一般情况下是只读的,这是对执行代码的一种保护机制。
  • .data段: 数据段通常用于存放程序中已初始化且初值不为0的全局变量和静态变量。数据段属于静态内存分配(静态存储区),可读可写。
  • .bss段: 未初始化以及初始为0的全局变量和静态变量,操作系统会将这些未初始化变量初始化为0
  • 堆(heap):用于存放进程运行时动态分配的内存。
    • 堆中内容是匿名的,不能按名字直接访问,只能通过指针间接访问。
    • 堆向高地址扩展(即“向上生长”),是不连续的内存区域。这是由于系统用链表来存储空闲内存地址,自然不连续,而链表从低地址向高地址遍历。
  • **内存映射区(mmap):**内存映射区域用于映射文件、共享内存等。这允许进程通过内存访问文件,而不必进行显式的文件 I/O 操作。
  • 栈(stack): 存储函数内部声明的非静态局部变量,函数参数,函数返回地址等信息,栈内存由编译器自动分配释放。栈和堆相反地址“向下生长”,分配的内存是连续的。
  • 命令行参数:存储进程执行的时候传递给main()函数的参数,argc,argv[]
  • 环境变量: 存储和进程相关的环境变量, 比如: 工作路径, 进程所有者等信息

你可能感兴趣的:(linux,服务器,linux)