Virtual Memory
2014年1月17日
虚拟内存一种抽象的形式,它使得每一个进程都以为自己独占整个主存。每一个进程的内存分布都有统一的视图,被称为它的虚拟地址空间。
Linux的虚拟地址空间如图1.13所示。(其它的Unix系统也使用类似的布局。)
在Linux中,操作系统把虚拟地址空间最顶端的区域(译者注:内核空间)保留用于所有进程的公共代码和数据。地址空间的低端区域(译者注:用户空间)被用户进程用来存储代码和数据。注意表格中的地址空间增长方向是从下到上的。
(译者注:在Linux系统中(32位),0x00000000~0xbfffffff为用户空间,0xc0000000~0xffffffff为内核空间。)
每一个进程看到的虚拟地址空间是由一些被划分好的用于特定目的的区域组成。在本书的随后部分你会获取到更多关于这些区域的细节,但是简略地从低端开始向上看一下每个区域也是很有帮助的。
- Program code and data.所有的进程的代码都从相同固定的地址开始,接下来是数据区域,相当于C语言中的全局变量。代码和数据是直接用可执行文件的内容进行初始化,在我们例子里是hello文件。在第7章我们学习链接和加载时会学习更多的关于地址空间的这部分内容。
- Heap.在代码和数据区域之后紧跟着运行时的堆。代码和数据区域在程序开始运行一次性分配固定大小的区域,与它不同的是,堆的扩张和缩小是动态的,它是调用C标准库如malloc和free之后的运行结果。我们在第9章学习虚拟内存管理时将学习更多的关于堆的细节。
- Shared libraries.大概在地址空间的中间部分的区域用于装载了共享库如C标准库和数学库的代码和数据。共享库的概念非常强大但比较难以理解。你将会在第7章学习动态链接时了解它们是如何工作的。
- Stack.在用户虚拟内存地址空间的最顶端是用户栈,编译器利用它来实现函数调用。跟堆一样,在程序运行用户栈的扩张和缩小也是动态的。特别是我每们调用一次函数时,栈就是增长。每次函数返回时,栈就是缩小。你会在第3章学习到编译器如何使用栈。
- Kernel virtual memory.内核是操作系统的一部分,它总是长驻于内存。地址空间的最顶端区保留给内核使用。应用程序不允许读写这部分区域和直接调用内核代码段里定义的函数。
正常运作虚拟内存需要硬件和操作系统软件一系列的复杂交互,包括对处理器使用的每一个地址进行硬件转换。基本的思想是将进程的虚拟内存的内容存储在磁盘上,把主存当作磁盘的缓存使用。第9章将会介绍它的工作原理并解释为什么它对于现代操作是如此的重要。