Linux 和对称多处理(转载)

Linux 和对称多处理

在 SMP 系统上发挥 Linux 的功能

如主流*处理器(CPU)供应商所证明的那样,多核处理器随时准备着占领桌面和嵌入式市场。多处理带来了更高的性能,也同时带来了新的问题。本文将探讨多处理和开发使用 SMP 的 Linux? 应用程序背后的思想。

M. Tim Jones (mtj@mtjones.com), 顾问工程师, Emulex

2010 年 9 月 20 日

  • Linux 和对称多处理(转载)内容

可以通过多种方法提高 Linux 系统的性能,而其中最流行的一种是提高处理器的性能。一个明显的解决方案是使用具有更快时钟频率的处理器,但是对于任何特定技术来讲都存在一个物理极限,时钟频率也有这样的极限。当达到那个极限时,可使用 “越多越好” 的方法应用多处理器。不幸的是,多处理器的性能并不与单个处理器性能的总和成线性比例。

在开始讨论 Linux 中的多处理应用程序之前,我们先来快速地回顾一下多处理的历史。

多处理的历史

多处理起源于 20 世纪 50 年代中期的一些公司,这些公司中有些您可能知道,而另一些您可能就不记得了(IBM、Digital Equipment Corporation、Control Data Corporation)。20 世纪 60 年代早期,Burroughs Corporation 引入了一种对称 MIMD 多处理器,它带有四个 CPU 并通过交叉开关可连接最多十六个内存模块(第一种 SMP 架构)。1964 年引入了 CDC 6600,它的使用比较成功并得到流行,它提供了一个带有十个子处理器(外围处理单元)的 CPU。20 世纪 60 年代末,Honeywell 发布了它的第一个 Multics 系统,这是带八个 CPU 的另一种对称多处理系统。

在开发多处理系统的同时,各种技术的使用也提高了缩小处理器体积和运行更快的时钟频率的能力。20 世纪 80 年代,Cray Research 等公司引入了多处理器系统和类似 UNIX? 的操作系统(CX-OS),以便利用这些能力。

20 世纪 80 年代末期,随着单处理器个人计算机系统(如 IBM PC)的流行,多处理系统的使用呈下降趋势。但是到了二十年后的现在,多处理利用对称多处理技术又回到了个人计算机系统中。

Amdahl 法则

Gene Amdahl 是一名计算机架构师、IBM 职员,在 IBM、Amdahl Corporation(以他的名字命名的企业)和其他一些公司从事计算机架构开发。但是最著名的是他的法则,该法则用于在改进系统的一部分后预测最大的预期系统改进。它主要用来计算使用多处理器后理论上的最大性能改进(参见图 1)。

图 1. 处理器并行化的 Amdahl 法则
Linux 和对称多处理(转载)

使用图 1 所示的等式,可计算系统的最大性能改进,N 表示处理器的数目,而因数 F 指定不能并行化的系统部分(即本质上顺序的系统部分)。结果如图 2 所示。

图 2. 最多十个 CPU 的 Amdahl 法则
Linux 和对称多处理(转载)

图 2 中最上面的一条线显示了处理器的数目。理想状态下,添加另外的处理器来解决问题时,希望看到这样的性能增长。不幸的是,并非所有的问题都可以并行化,而且还有管理处理器的开销,所以速度的提高并没有这么大。底部(紫色的线)是一个 90% 的处理属于顺序性的问题例子。在此图中,最佳的情况是棕色的线,它展示了一个 10% 顺序性(因此 90% 可并行化)的问题。即使在这种情况下,十个处理器的执行性能也只比五个稍好一点儿。

 

多处理和 PC

SMP 架构:两个或多个同样的处理器通过一块共享内存彼此连接。每个处理器可同等地访问共享内存(具有相同的内存空间访问延迟)。可将此架构与 Non-Uniform Memory Access(NUMA)架构进行对比。例如,每个处理器拥有自己的内存,访问共享内存时具有不同的访问延迟。

松耦合多处理

最早的 Linux SMP 系统是松耦合多处理器系统。这些系统是利用多个高速互连的单一系统构造的(如 10G 以太网、Fibre Channel 或 Infiniband)。此类架构也被称作集群(参见图 3),Linux Beowulf 项目是此类架构的一个流行的解决方案。Linux Beowulf 集群可利用普通硬件和典型的网络互连(如以太网)来构建。

图 3. 松耦合多处理架构
Linux 和对称多处理(转载)

构建松耦合多处理器架构很容易(由于使用了 Beowulf 之类的项目),但是它们也有自身限制。构建大型的多处理器网络可能占用相当大的空间并消耗很多电量。因为它们通常是利用普通硬件来构建的,所以包含的有些硬件不相关却要耗费很多电量和空间。更大的缺点在于通信结构。即使使用高速网络(如 10G 以太网),也存在系统可伸缩性的限制。

紧密耦合多处理

紧密耦合多处理指芯片级多处理(CMP)。可将其看作松耦合架构按比例缩小至芯片级。这正是紧密耦合多处理(也称作多核计算)背后的思想。在一个集成电路中,多个芯片、共享内存以及互连形成了一个紧密集成的多处理核心(参见图 4)。

图 4. 紧密耦合多处理架构
Linux 和对称多处理(转载)

在 CMP 中,多个 CPU 通过共享总线连接到共享内存(2 级缓存)。每个处理器也拥有自身的快速内存(1 级缓存)。CMP 紧密耦合的本质使处理器与内存之间的物理距离很短,因此可提供最小的内存访问延迟和更高的性能。此类架构在多线程应用程序中运行良好,该类应用程序中线程可能被分配给多个处理器以实现并行操作。这种方法被称作线程级并行(TLP)。

由于这种多处理器架构的流行,很多供应商都生产了 CMP 设备。表 1 列出了一些 Linux 支持的流行变种。

表 1. CMP 设备样例
供应商 设备 说明
IBM POWER4 SMP,双 CPU
IBM POWER5 SMP,双 CPU,四个并发线程
AMD AMD X2 SMP,双 CPU
Intel? Xeon SMP,双 CPU 或四 CPU
Intel Core2 Duo SMP,双 CPU
ARM MPCore SMP,最多四 CPU
IBM Xenon SMP,三个 Power PC CPU
IBM Cell Processor 不对称多处理(Asymmetric multiprocessing,ASMP),九 CPU
 

内核配置

为了在支持 SMP 的硬件上通过 Linux 使用 SMP,必须适当地配置内核。内核配置期间必须启用 CONFIG_SMP 选项,以使内核感知 SMP。通过在多 CPU 主机上运行感知 SMP 的内核,可使用 proc 文件系统了解处理器的数目及类型。

首先,使用 grep 在 /proc 下的 cpuinfo 文件中检索处理器的数目。如清单 1 所示,使用计数选项(-c)来统计以单词 processor 开头的行。然后显示 cpuinfo 文件的内容。显示的示例来自一个二芯片的 Xeon 主板。

清单 1. 使用 proc 文件系统检索 CPU 信息
mtj@camus:~$ grep -c ^processor /proc/cpuinfo
8
mtj@camus:~$ cat /proc/cpuinfo
processor       : 0
vendor_id       : GenuineIntel
cpu family      : 15
model           : 6
model name      : Intel(R) Xeon(TM) CPU 3.73GHz
stepping        : 4
cpu MHz         : 3724.219
cache size      : 2048 KB
physical id     : 0
siblings        : 4
core id         : 0
cpu cores       : 2
fdiv_bug        : no
hlt_bug         : no
f00f_bug        : no
coma_bug        : no
fpu             : yes
fpu_exception   : yes
cpuid level     : 6
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr 
pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm 
pbe nx lm pni monitor ds_cpl est cid xtpr

bogomips        : 7389.18

...

processor       : 7
vendor_id       : GenuineIntel
cpu family      : 15
model           : 6
model name      : Intel(R) Xeon(TM) CPU 3.73GHz
stepping        : 4
cpu MHz         : 3724.219
cache size      : 2048 KB
physical id     : 1
siblings        : 4
core id         : 3
cpu cores       : 2
fdiv_bug        : no
hlt_bug         : no
f00f_bug        : no
coma_bug        : no
fpu             : yes
fpu_exception   : yes
cpuid level     : 6
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr 
pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm 
pbe nx lm pni monitor ds_cpl est cid xtpr

bogomips        : 7438.33 

mtj@camus:~$
 

SMP 和 Linux 内核

在 Linux 2.0 的早期,SMP 支持由一个 “大锁” 组成,这个 “大锁” 对跨系统的访问进行串行化。对于 SMP 支持的改进缓慢地进行着,但是直到 2.6 内核才显示出 SMP 的威力。

2.6 内核引入了新的 O(1) 调度程序,它包含更好的 SMP 系统支持。关键在于能在可用 CPU 之间进行负载平衡,同时维持亲合性以提高缓存效率。回顾图 4,当任务与单个 CPU 相关联时,如果将其移到另一个 CPU,就需要为该任务清空缓存。这就增加了任务的内存访问延迟,这些时间用来将其数据移入新 CPU 的内存中。

2.6 内核为每个处理器维护两个 runqueue(过期的和活动的 runqueue)。每个 runqueue 支持 140 个优先级,前面的 100 个用于实时任务,而后面的 40 个用于用户任务。任务分时间片执行,在使用它们分配到的时间片时,这些任务被从活动的 runqueue 移至过期的 runqueue。这就为所有的任务提供了公平访问 CPU 的机会(仅根据每个 CPU 锁定)。

利用每个 CPU 的任务队列,可以根据系统中所有 CPU 的负载进行负载平衡。每 200 毫秒,调度程序就执行一次负载平衡调节,以便重新分配任务负载,维持处理器之间的平衡。有关 Linux 2.6 调度程序的更多信息,请参阅 参考资料 部分。

 

用户空间线程:使用 SMP 的功能

为使用 SMP,需要在 Linux 内核中执行大量工作,但仅靠操作系统本身还不够。回顾一下,SMP 的能力取决于 TLP。单个单片(非线程化的)程序不能使用 SMP,但是 SMP 可在由核心之间分布的多个线程组成的程序中使用。当一个线程由于等待完成 I/O 处理而被延迟时,另一个线程可执行一些有用的工作。这样,线程之间互相协作,隐藏了各自的延迟时间。

Portable Operating System Interface(POSIX)线程是构建可利用 SMP 的线程化应用程序的一种好方法。POSIX 线程提供了线程化机制和共享内存。当调用程序创建若干线程时,每个线程得到自身的堆栈(本地变量和状态),但共享父线程的数据空间。创建的所有线程共享这个相同的数据空间,但是问题就出在这里。

为支持多线程访问共享内存,需要具备协调机制。POSIX 提供了互斥函数来创建临界区,用于实施单线程对对象(一块内存)的独占访问。不这样做可能导致内存破坏(由于多个线程执行了不同步的操作)。清单 2 演示了如何使用 POSIX 互斥函数创建临界区。

清单 2. 使用 pthread_mutex_lock 和 pthread_mutex_unlock 创建临界区
pthread_mutex_t crit_section_mutex = PTHREAD_MUTEX_INITIALIZER;

...

pthread_mutex_lock( &crit_section_mutex );

/* Inside the critical section. Memory access is safe here
 * for the memory protected by the crit_section_mutex.
 */

pthread_mutex_unlock( &crit_section_mutex );

如果多个线程在完成上面的初始调用后尝试锁定信号量,则这些线程会被阻塞,它们的请求进入队列,直到执行 pthread_mutex_unlock 调用。

 

SMP 的内核变量保护

如果处理器中的多个核心为内核并发运作,则希望避免共享那些特定于给定核心的数据。出于此原因,2.6 内核引入了 per-CPU 变量的概念,这些变量与单个 CPU 相关联。这允许将某个 CPU 通常访问的变量声明为该 CPU 专有的变量。使用此方法使锁定需求最小化并提高了性能。

per-CPU 变量由 DEFINE_PER_CPU 宏进行定义,需要为该宏提供类型和变量名称。由于宏的运作方式与 l-value 类似,因此也可在宏中进行变量的初始化。下面的示例(来自 ./arch/i386/kernel/smpboot.c)定义了一个变量,它用于表示系统中每个 CPU 的状态。

/* State of each CPU. */
DEFINE_PER_CPU(int, cpu_state) = { 0 };

该宏创建了一个变量数组(每个 CPU 一个变量)。要访问 per-CPU 变量,需将 per_cpu 宏和 smp_processor_id 结合使用,后者是一个函数,用于返回当前执行代码的当前 CPU 的标识符。

per_cpu( cpu_state, smp_processor_id() ) = CPU_ONLINE;

内核提供了用于 per-CPU 锁定和变量动态分配的其他函数。可在 ./include/linux/percpu.h 中查找这些函数。

 

结束语

当处理器频率达到其极限时,一种流行的提高性能的方法是添加更多的处理器。在早期,这就意味着将更多的处理器添加到主板上,或将多个独立计算机集群到一起。现在,芯片级多处理能够在单个芯片上提供更多的 CPU,由于减少了内存延迟,因而可获得更高的性能。

您会发现 SMP 系统不仅存在于服务器中,还存在于桌面上,特别是在引入虚拟化以后。跟大多数先进技术一样,Linux 提供 SMP 支持。内核负责完成可用 CPU 间的负载优化(从线程到虚拟化操作系统)。惟一要做的就是确保应用程序可被充分地多线程化以便使用 SMP 的能力。

Linux 和对称多处理(转载),布布扣,bubuko.com

Linux 和对称多处理(转载)

上一篇:微信小程序 图片等比例 适应屏幕尺寸


下一篇:如何解决Maven依赖本地仓库eclipse报错的问题