操作系统的内存管理

内存管理子系统是操作系统最重要的部分之一。从早期计算开始,系统的内存大小就难以满足人们的需要。为了解决这个问题,可利用虚拟内存。虚拟内存通过当需要时在竞争的进程之间共享内存,使系统显得有比实际上更多的内存空间。

虚拟内存不仅仅使机器上的内存变多,内存管理子系统还提供以下功能:

  • 大地址空间
    操作系统使系统显得它有比实际上大得多的内存。虚拟内存可以比系统中的物理内存大许多倍。
  • 保护
    系统中每个进程有自己的虚拟地址空间。这些虚拟地址空间相互之间完全分离,所以运行一个应用的进程不能影响其他的进程。同样,硬件的虚拟内存机制允许内存区域被写保护。这样保护了代码和数据不被恶意应用重写。
  • 内存映射
    内存映射用来把映像和数据文件映像到一个进程的地址空间。在内存映射中,文件的内容被直接链接到进程的虚拟地址空间。
  • 公平物理内存分配
    内存管理子系统给予系统中运行的每个进程公平的一份系统物理内存。
  • 共享虚拟内存
    尽管虚拟内存允许进程拥有分隔的(虚拟)地址空间,有时你会需要进程共享内存。例如系统中可能会有几个进程运行命令解释shell bash。最好是在物理内存中只有一份bash拷贝,所有运行bash的进程共享它;而不是有几份bash拷贝,每个进程虚拟空间一个。动态库是另一个常见的几个进程共享执行代码的例子。

Linux操作系统的存储管理

Linux进程采用动态地址映射方式,进程的地址空间和存储空间的对应关系是在程序的执行过程中实现的。进程使用的是虚拟地址,因此,他对每个地址的访问都需要通过MMU把虚拟地址转换为内存的物理地址。
Linux操作系统采用了请求式分页虚拟存储管理方法。系统为每个进程提供了4GB的虚拟内存空间。各个进程的虚拟内存彼此独立。其中0-3GB为用户空间,用户态进程可以直接访问此空间。3-4GB为内核空间,存放内核访问的代码和数据,用户进程不能直接访问,用户进程只能通过中断或者系统调用进入内核态时才有权访问。在Linux系统中,内核空间一直处于当前状态并且在所有进程中映射到相同的物理内存
Linux的存储管理主要是管理进程虚拟内存的用户区。进程虚拟内存的用户区分为:代码段、数据段、堆栈,以及进程运行的环境变量、参数传递区域等。

虚拟内存的空间,进程使用多少,操作系统就会分配多少,并且将这些虚拟地址映射到物理地址,没有使用的地址保留。

蓝色区域代表已经映射物理内存的虚拟地址,白色区域为没映射部分。在上面的例子中,Firefox由于他的巨大的内存需求,已经使用了他的大部分虚拟地址空间。地址空间中不同的带对应内存段如堆、栈等等。

栈:进程地址空间中最上面的段为栈,很多语言中栈用于存储本地变量和函数参数。调用一个方法或函数时压入栈一个新的栈帧。当函数返回时,这个压入的栈帧被释放。这个简单的设个,可能是因为数据遵循严格的FIFO次序,这意味着再复杂的数据结构都无需跟踪栈内容——一个简单的栈顶指针将会做跟踪作用。这样入栈和出栈非常快速和准确。进一步,堆栈地区不断重用,往往在CPU缓存中持有活跃的栈内存,加快存取。进程中的每个线程获得他自己的栈。
BSS段、数据段和代码段:BSS段和数据段在C语言中存储静态和全局变量。不同的是BSS段存放的是没有被初始化的静态变量,也就是所这些静态变量在源代码中没有被程序员设置初值。BSS内存区是匿名的:他不映射任何特定的文件。

Window操作系统的内存管理

Window也为每个进程分配了4GB的虚拟地址空间,让每个进程都认为自己拥有4GB的内存空间,32位CPU可以取地址的空间为2的32次方,就是4GB(正如16位CPU有20根寻址线所有拥有2的20次方的寻址空间一样)。当我们在Windows中运行一个程序之后,系统会为该应用程序创建一个进程,Windows使得每个进程都拥有2GB的地址空间,这2GB地址空间用于程序存放代码,数据,堆栈,自由存储区(堆),另外2GB用于共享系统使用。

上图中,最左边的地址都是虚拟地址。虚拟空间只是Windows为该进程分配的一个虚拟的地址空间,只有当其和物理内存相关联后才有意义。