我打算写几篇有关计算机内部的文章,以期能够解释清楚现代内核的工作原理。对这些事物感兴趣的爱好者和程序员往往缺乏经验,我希望对这些人能有所帮助。我们把重点放在 Linux,Windows 和 Intel 处理器上。探究内部构造是我的一个爱好,我曾经写过了很多运行在内核模式(kernel mode)下的代码,但是有很长时间没写过了,第一篇文章就让我们来描述下基于 Intel 的主板结构布局、CPU 如何访问内存以及系统内存映射吧。

首先,让我们来看下当今 Intel 计算机的各部件是如何连接的吧。下图展示了主板的主要组件,并辅以难看的配色。

Mother Board Diagram

现代主板示意图,北桥和南桥组成了芯片组。

看着这张图,你要记住的关键是 CPU 对于它连接的东东一无所知。CPU 通过它的 引脚 与外界通信,而不关心外界为何物,可能是计算机里的一块主板,也可以是一个烤箱、网络路由器、脑机接口,抑或一个 CPU 测试平台。CPU 与外部通信主要有 3 种方式:内存地址空间,I/O 地址空间和中断。目前,我们只关心主板和内存。

在主板上,CPU 与外部世界的关口是一端连接着北桥的前端总线(Front-side Bus),CPU 只要读写内存,它就得通过这个总线。CPU 使用一些引脚发送它要读写的物理内存地址,而其他引脚则负责收发读写数据。Intel Core 2 QX6600 处理器有 33 个引脚用来传送物理内存地址(所以,它可以访问 233 个存储单元)和 64 个引脚用来收发读写数据(因此,数据是以 64 位或 8 字节块传输的)。这可以让 CPU 寻址 64GB(233 * 8 字节)的物理内存尽管大部分芯片组只能处理 8GB RAM。

现在问题来了,我们习惯于只从 RAM 的角度来考虑内存,即程序一直在读写的东东。诚然,来自程序的大部分内存请求都通过北桥路由到了 RAM 模块,但是,大部分不等于全部。物理内存地址也用于和主板上各式各样的设备通信(这种通信被称为 内存映射 I/O)。这些设备包括显卡、大多数 PCI 卡(比如扫描仪或SCSI 卡),也包括存储 BIOS 的 闪存。

当北桥收到了一个物理地址请求时,它将裁决该物理地址去往何处:RAM?或显卡?这个路由策略是由内存地址映射决定的。对于物理内存地址的每个区域,内存映射晓得该区域属于哪个设备。大部分的地址都被映射到 RAM 中,但是当它们没有被映射时,内存映射就会告诉北桥哪个设备应该为这些地址提供请求服务。这些远离 RAM 模块的内存地址映射造成了 PC 内存中 640KB~1MB 之间的历史空洞。当若为显卡和 PCI 设备预留内存地址时,空洞就会更大。这就是为什么 32 位操作系统 使用 4GB 的 RAM 会出现问题 的原因。Linux 的 /proc/iomem 文件中齐刷刷地列出了这些地址范围映射。下图显示了英特尔 PC 中前 4GB 物理内存地址的典型内存映射:

Memory Layout

英特尔系统中前 4 GB 的内存布局

实际的地址和范围取决于具体的主板和电脑中存在的设备,但大多数 Core 2 系统都很接近上述布局。所有的棕色区域都被映射到远离 RAM 的地方。请记住,这些是用在主板总线上物理地址。在 CPU 内部(例如,在我们运行和编写的程序中),内存地址指的是逻辑地址,它们必须在内存在总线上访问之前被 CPU 翻译成物理地址。

逻辑地址到物理地址的转换规则相当复杂,这依赖于 CPU 正运行在哪种模式(mode)下(实模式、32 位保护模式、64 位保护模式)。无论翻译机制如何,CPU 模式决定了可以访问多少物理内存。例如,如果 CPU 正运行在 32 位保护模式下,它只能寻址到 4GB(当然了,PAE 是个例外,我们暂且不论)。由于高 1GB 左右的物理地址被映射到了主板其他设备上,所以 CPU 只能有效地使用大约 3GB 的 RAM(有时更少--我有一台 Vista 机器,它只有 2.4GB 可用)。如果 CPU 处于 实模式 下,那么它只能访问 1MB 的物理内存(这是早期英特尔处理器能够提供的唯一模式)。话分两头,运行在 64 位模式下的 CPU 可以物理寻址 64GB 的空间(尽管很少有芯片组支持那么大的 RAM)。在 64 位模式下,可以利用系统总内存以上的物理地址来访问被主板设备偷走的那些物理地址所对应的 RAM 区域,这就是所谓的回收内存(reclaiming memory),它是在芯片组的帮助下完成的。

以上就是下一篇文章所需要的有关内存的全部预备知识了,下篇文章描述了从开机到 BootLoader 将跳入内核的启动过程。