程序地址空间 为什么存在虚拟地址空间

当一个程序加载到内存,操作系统会给他分配自己的虚拟地址空间。虚拟地址空间主要分为两部分,分别是内核空间和用户空间。在Linux下内核空间一般是1G大小,用户空间4G大小。内核空间是所有的进程共享,随时准备处理系统调用和异常的操作。用户空间是每一个进程独立的部分,同时保证了虚拟地址空间的独立性,通过mm_struct结构体进行描述,用户空间下的每个段都是通过vm_area_struct结构体描述。一个CPU统一时刻只能处理一个虚拟地址空间并且进行调度切换。

在Linux下面查看一个进程的虚拟地址空间信息通过:cat proc/pid/maps

用户空间:地址从大到小分为栈、共享区、堆、未初始化的数据、初始化的数据、代码段。栈主要存储函数参数、返回值和函数内部非静态局部变量等。堆内存在C语言中通过malloc等申请的内存空间,C++中是new申请的内存空间,并且堆内存需要用户自己手动释放。共享区可以装载一个共享的动态库,通过系统调用创建共享内存实现进程间通信。数据段分为以初始化和未初始化,主要存储全局变量和静态变量。代码段主要存储字符串常量,这个区域只具有可读性。

上述我们提到了虚拟地址空间和程序地址空间。那么这两者之间有什么关系?

程序地址空间就是虚拟地址空间,两者都不是真正的地址空间。真正的地址空间是物理内存。那为什么会有现代技术的虚拟地址空间呢?虚拟地址空间的出现主要下面原因:

操作系统管理进程地址空间,其实管理的是虚拟地址空间,我们所写的代码打印出来的地址都是虚拟地址,物理地址是不允许我们访问的。那么为什么是虚拟地址空间,这就涉及到了早期地址空间布局。

早期内存管理机制

早期运行一个程序,会把这个程序全部装载到内存,所以计算机想要运行一个程序,必须要有足够的内存。如果内存大小100M,可执行程序A大小为25M,可执行程序B大小为50M,可执行程序C大小为30M,现在内存中运行程序A和B,如果C想要运行,必须要换出某个程序,导致数据在内存和磁盘之间来回拷贝,效率太慢。

综上所述,可以看出早期的内存管理机制问题:

  1. 进程地址空间不隔离。每一个程序都是不独立运行在内存中,所以如果有恶意程序,可能会导致程序出现问题。
  2. 内存使用效率太低。如果某一个程序想要运行,可能会需要换出某个程序,导致数据的来回拷贝。其实与连续分配有关
  3. 程序运行的地址不确定。由于程序运行内存是随机分配的,所以程序的地址不确定。

改进

现代操作系统是通过分段和分页来进行内存管理。

分段:cpu通过段寄存器将内存分为不同的段,所以指令和数据的有效地址并不是真正的物理地址,而是首地址加偏移地址。(1)因为段寄存器的存在,使得每个程序的地址空间隔离,所以越界问题,很容易判断出来。(2)实际代码和数据的地址都是偏移量,所以也得以确定。

但是分段并没有解决内存使用效率问题。

分页:分页管理主要针对物理内存和虚拟地址空间内存管理和映射机制。通过分页,进程实际所得到的物理内存是不连续的。但是通过虚拟内存机制,被看成是连续分配的了。

虚拟内存和物理内存之间通过页表(MMU)来进行映射,所以可以看出页表主要作用是解决映射,除此之外还有保证内存的访问权限,如字符串存储在代码段,只具有可读性。

综上,虚拟内存:

  1. 给所有进程提供一致的地址空间,每个进程都认为自己是在独占使用单机系统的存储资源
  2. 保护每个进程的地址空间不被其他进程破坏,隔离了进程的地址访问
  3. 分页管理主要针对物理内存和虚拟地址空间内存管理和映射机制。通过分页,进程实际所得到的物理内存是不连续的。但是通过虚拟内存机制,被看成是连续分配的了,提高了内存的利用率。

你可能感兴趣的:(操作系统)