我打算写一组讲述计算机内幕的文章,旨在揭示现代操作系统内核的工作原理。我希望这些文章能对电脑爱好者和程序员有所帮助,特别是对这类话题感兴趣但没有相关知识的人们。讨论的焦点是Linux,Windows,和Intel处理器。钻研系统内幕是我的一个爱好。我曾经编写过不少内核模式的代码,只是最近一段时间不再写了。这第一篇文章讲述了现代Intel主板的布局,CPU如何访问内存,以及系统的内存映射。 作为开始,让我们看看当今的Intel计算机是如何连接各个组件的吧。下图展示了主板上的主要组件:
现代主板的示意图,北桥和南桥构成了芯片组。
当你看图时,请牢记一个至关重要的事实:CPU一点也不知道它连接了什么东西。CPU仅仅通过一组针脚与外界交互,它并不关心外界到底有什么。可能是一个电脑主板,但也可能是烤面包机,网络路由器,植入脑内的设备,或CPU测试工作台。CPU主要通过3种方式与外界交互:内存地址空间,I/O地址空间,还有中断。
眼下,我们只关心主板和内存。安装在主板上的CPU与外界沟通的门户是前端总线(front-side bus),前端总线把CPU与北桥连接起来。每当CPU需要读写内存时,都会使用这条总线。CPU通过一部分管脚来传输想要读写的物理内存地址,同时另一些管脚用于发送将被写入或接收被读出的数据。一个Intel Core 2 QX6600有33个针脚用于传输物理内存地址(可以表示233个地址位置),64个针脚用于接收/发送数据(所以数据在64位通道中传输,也就是8字节的数据块)。这使得CPU可以控制64GB的物理内存(233个地址乘以8字节),尽管大多数的芯片组只能支持8GB的RAM。
现在到了最难理解的部分。我们可能曾经认为内存指的就是RAM,被各式各样的程序读写着。的确,大部分CPU发出的内存请求都被北桥转送给了RAM管理器,但并非全部如此。物理内存地址还可能被用于主板上各种设备间的通信,这种通信方式叫做内存映射I/O。这类设备包括显卡,大多数的PCI卡(比如扫描仪或SCSI卡),以及BIOS中的flash存储器等。
当北桥接收到一个物理内存访问请求时,它需要决定把这个请求转发到哪里:是发给RAM?抑或是显卡?具体发给谁是由内存地址映射表来决定的。映射表知道每一个物理内存地址区域所对应的设备。绝大部分的地址被映射到了RAM,其余地址由映射表来通知芯片组该由哪个设备来响应此地址的访问请求。这些被映射为设备的内存地址形成了一个经典的空洞,位于PC内存的640KB到1MB之间。当内存地址被保留用于显卡和PCI设备时,就会形成更大的空洞。这就是为什么32位的操作系统无法使用全部的4GB RAM。Linux中,/proc/iomem这个文件简明的列举了这些空洞的地址范围。下图展示了Intel PC低端4GB物理内存地址形成的一个典型的内存映射:
Intel系统中,低端4GB内存地址空间的布局。
实际的地址和范围依赖于特定的主板和电脑中接入的设备,但是对于大多数Core 2系统,情形都跟上图非常接近。所有棕色的区域都被设备地址映射走了。记住,这些在主板总线上使用的都是物理地址。在CPU内部(比如我们正在编写和运行的程序),使用的是逻辑地址,必须先由CPU翻译成物理地址以后,才能发布到总线上去访问内存。
这个把逻辑地址翻译成物理地址的规则比较复杂,而且还依赖于当时CPU的运行模式(实模式,32位保护模式,64位保护模式)。不管采用哪种翻译机制,CPU的运行模式决定了有多少物理内存可以被访问。比如,当CPU工作于32位保护模式时,它只可以寻址4GB物理地址空间(当然,也有个例外叫做物理地址扩展,但暂且忽略这个技术吧)。由于顶部的大约1GB物理地址被映射到了主板上的设备,CPU实际能够使用的也就只有大约3GB的RAM(有时甚至更少,我曾用过一台安装了Vista的电脑,它只有2.4GB可用)。如果CPU工作于实模式,那么它将只能寻址1MB的物理地址空间(这是早期的Intel处理器所支持的唯一模式)。如果CPU工作于64位保护模式,则可以寻址64GB的地址空间(虽然很少有芯片组支持这么大的RAM)。处于64位保护模式时,CPU就有可能访问到RAM空间中被主板上的设备映射走了的区域了(即访问空洞下的RAM)。要达到这种效果,就需要使用比系统中所装载的RAM地址区域更高的地址。这种技术叫做回收(reclaiming),而且还需要芯片组的配合。 |