当我们试图去了解复杂系统时,去除其抽象层,直接关注最底层,我们会更容易去理解。使用这种方法,我们来看一下内存和 I/O 接口的最简单和基础的层:处理器和总线的接口。这些细节是更上层问题的基础,例如线程同步、Core i7 的需求等。然而,由于我是一个程序员,我将忽略一些 EE 人关注的问题。下面展示的是典型的 Core 2 架构:
Core 2 处理器有 775 个引脚,大约一半仅仅提供电力,并不传输数据。当你按功能对这些引脚进行分组时,你会惊奇地发现处理器的物理接口非常简单。图示的是涉及到内存或 I/O 口操作的关键引脚:地址线、数据线以及请求引脚。这些操作都发生在前端总线(Front Side Bus)事务(Transation)上下文中。FSB 事务经历五个阶段:仲裁、请求、侦听、响应以及数据传输。在这些不同的阶段,FSB 上的组件分别扮演了不同的角色,我们称之为代理(agents)。通常这些代理是所有的处理器单元加上北桥。
这篇文章中我们仅仅看一下 请求阶段 —— 由 请求代理 (通常是处理器)发送两个数据包。下面是第一个数据包的数据位,由地址和请求引脚发出:
地址线输出的是事务的起始物理地址,33 位地址引脚线对应的是一个地址的的 3~35 位,地址的 0~2 位为零。因此,对于寻址范围为 64GB 的物理内存,我们得到一个 8 字节对齐的 36 位的地址,这是从 Pentium Pro 处理器之后就这样的。请求引脚表示需要初始化的事务类型,而在 I/O 请求事务中,地址引脚给出的是 I/O 端口而不是内存地址。在第一个数据包输出之后,在接下来的总线时钟周期内又传送了第二个数据包:
属性信号位(Attribute Signals)非常有意思:在 Intel 处理器中,它们代表的了 5 种可用的内存 cache 行为。通过将这个信息传送到 FSB 上,请求代理让其它处理器知道该事务如何影响 cache 的,以及内存控制器(北桥)应该做什么。处理器通过查看页表来决定给定内存区块的类型,页表是由内核来维护的。
通常内核将所有的内存视作写回类型(Write-Back),会获得最好的性能。在写回模式下,内存单元访问的是 cache 行,在 Core 2 中是 64 字节。如果程序读内存的一个字节,处理器加载包含该字节的整个 cache 行到 L2 和 L1 cache 中。当程序写回到内存中,处理器仅仅修改 cahce 行上的数据,而不立刻更新主存。接下来,当它需要将修改的行提交到总线上时,一致性地将整个 cache 行写回。所以,大多请求引脚在 Length 域填写 11,表示请求 64 个字节, 。下面是一个读不在 cache 中的数据的实例:
在 Intel 计算机中将一些物理内存直接映射到硬盘和网卡等设备上,这就允许驱动器通过直接读写内存来与它们的设备通信。内核在页表中将这些内存区域标记为 不可缓存的( uncacheable)。程序或者驱动器请求访问不可缓存的内存区域直接作用在总线上。读写一个字节、一个字等等都是有可能的,这是通过填写上面的数据包 B 的 Byte Enable 字段来完成的。
这里讨论的有很多隐式含义,例如:
- 性能敏感的应用应该尝试将数据打包到同一个 cache 行,这样可以一起被访问。一旦 cache 行加载后,接下来的读就避免了从 RAM 中访问,访问速度更快!
- 任何对同一 cache 行的内存访问都确保要是原子操作(假设是写回内存模式),这样的一次访问由处理器的 L1 cache 来完成,数据读或写都是一次完成的,不可被其它的处理器或线程来干扰。通常,32 位和 64 位不跨 cahe 行的访存操作都是原子的。
- 前端总线由所有的代理共享,在开始一个事务之前需要仲裁总线的所有者。另外,所有的代理为了维护数据的一致性,必须侦听总线上的所有事务。因此,在越来越多的处理器核的添加到 Intel 计算机中,争夺总线资源将成为一个很严重的问题。在 Core i7 处理器中,通过将处理器直接关联到内存上,点对点的通信代替广播来解决这个问题。
这些都是请求物理内存需要关注的重点问题。在接下来将锁、多线程以及内存一致性联系起来之后,总线问题又会浮现出来。我第一次看到 FSB 包描述时,我恍然大悟!我希望你们可有同样的感受。在下一篇文章中,我们爬上抽象层的梯子,看看虚拟内存的全貌。
注:
- 偶尔看到作者博客,里面的文章我很是喜欢,翻译也只是我来理解作者文章的内容,不建议你们看我翻译,直接看 原文。
- 如果大家发现我哪地方理解有偏差,欢迎指正。
- 这是一篇关于在处理器体系层次上如何实现访问物理内存的文章,作为一名程序员,我们确实很少关心这个层次的东西,而了解这些对于我们提升自己代码质量有很多帮助。
- 下一篇我将阅读作者的 Anatomy of a Program in Memory,这是比处理器层次更上一层,讲述一个进程的虚拟内存如何布局的。