SMP系统的实现需要软件和硬件协同完成。作为硬件来说,组成SMP系统的CPU需要支持处理器间的通信,需要硬件提供机制来维护CUP之间Cache内容的一致性等;而作为软件的OS来说,需要配合硬件来实现进程在各个CPU间的调度,处理各种外部中断等工作。
1、处理器间的同步与互斥
一般而言,只要能保证对临界资源操作的“原子性”,互斥性就可保证,单处理器系统正是基于这样的机理;在单处理器系统中,能够在单条指令中完成的操作被认为是“原子操作”。但在SMP结构中,由于系统中有多个处理器在独立运行,即使能在单条指令中完成的操作也可能受到干扰。与单处理器结构相比,SMP结构对互斥操作的微观“分辨率”需更高,有些在单处理器结构中的“原子操作”在SMP结构中不再是原子的了。
解决办法:单纯的读或写本来就是原子的,问题在于一些既要读又要写,需要两个或以上的微操作才能完成的指令,i386 CPU提供了在指令执行期间对总线加锁的手段。CPU芯片上有一条引线LOCK,如果汇编程序中在一条指令前加上前缀“LOCK”,汇编后的机器代码就会使CPU在执行这条指令时把引线LOCK的电位拉低,从而把总线锁住,同一总线上别的CPU就暂时不能通过总线访问内存了。有个特例:在执行指令xchg时CPU会自动将总线锁住,而不需要在程序中使用前缀“LOCK’’。xchg指令将一个内存单元中的内容与一个寄存器的内容对换,因此常常用于对内核信号量(semaphore)的操作。
2、Cache与内存间的一致性问题
Cache在SMP结构中,情况相对于单处理器系统更为复杂,因为一个CPU并不知道别的CPU会在何时改变内存的内容。Cache写操作有两种模式:“穿透”(Write-Through)模式,Cache对写操作好像不存在一样,每次写时都直接写到内存中,实际上只是对读操作使用Cache,因而效率相对较低;“回写”(Write-Back)模式,写的时候先写入Cache,然后由Cache硬件在周转时使用缓冲线自动写入内存,或由软件主动地“冲刷”有关的缓冲线。因此在改变了缓冲页面的内容,并启动DMA写操作将其写入磁盘前要先“冲刷”Cache中有关的缓冲线,因为改变了的内容可能还没有回写到内存缓冲区中。
在Intel Pentium CPU中有个寄存器,称为“存储类型及范围寄存器”(Memory Type Range Register,MTRR),通过这个reg可以将内存中的不同区间设置成使用或不使用Cache,以及对于写操作采用穿透模式或回写模式。
Cache的运用有可能改变对内存操作的次序。假定有两个观察者,一个观察CPU内部Cache受到访问的次序,另一个观察内存受到访问的次序,则二者可能会有相当大的差异。前者就是程序中编排好的次序,称作“指令序”(program ordering),后者则是实际出现在处理器外部,即系统总线上的次序,称作“处理器序”(process ordering)。不使用Cache时二者相同,如果使用Cache要看具体的情况和操作。如果保证“处理器序”与“指令序”相同,称作“强序”(strong ordering);反之,如果“处理器序”有时候可能不同于“指令序”,称作“弱序”(weak ordering)。对于单处理器结构的系统,这二者的不同并不成什么问题,然而对SMP结构的系统却可能成为问题。
单处理器系统中的DMA操作都是由设备驱动程序主动地启动的,所以设备驱动程序知道什么时候应该丢弃哪些缓冲线的内容,什么时候应该冲刷哪些缓冲线的内容。可在SMP结构中,每个CPU都可能改变内存中的内容,且异步改变,每个CPU都只知道自己何时会改变内存的内容,但不知道别的CPU什么时候改变内存的内容,也不知道本地Cache中的内容是否已经与内存中不一致,每个CPU也可能因为改变了内存的内容而使其他CPU的Cache变得不一致。
解决办法:对于Cache中的内容,一般只有数据才有一致性的问题,因为对指令一般都是只读,不在运行的过程中动态地加以改变。Intel在Pentium CPU中为已经装入Cache的数据提供了一种自动与内存保持一致的机制,称为“窥探”(Snooping)。每个CPU内部有一部分专门的硬件,一旦启用了Cache后就时刻监视系统总线上对内存的操作。由于对内存的操作定要经过系统总线,没有一次实际访问内存的操作能够逃过监视。如果发现有来自其他CPU的写操作,而本CPU的Cache中又缓冲存储着该次写操作的目标,就会自动把相应的缓冲线废弃,使得在需要用到这些数据时重新将其装入Cache,达到二者一致。这样,SMP结构中Cache与内存的数据一致性问题对软件而言就透明了。
3、中断处理
在单处理器结构中,整个系统只有一个CPU,所有的中断请求都由这个CPU响应和处理,而SMP结构不能固定让其中的某一个CPU处理所有的中断请求,否则其他CPU连时钟中断不能处理,这样如果在那些CPU上运行的进程陷入了死循环,就永远没有机会进行系统调用,这些CPU将永远不会有进程调度。另外,如果是让所有的CPU轮流处理中断,或谁空闲谁处理,中断请求的分配将如何处理,这些都需要软件和硬件协同来完成。
传统的i386处理器采用8259A中断控制器。一般而言,8259A的作用是提供多个外部中断源与单一CPU间的连接。如果在SMP结构中还是采用8259A中断控制器,就只能静态地把所有的外部中断源划分成若干组,分别把每一组都连接到一个8259A,而825A9则与CPU一对一连接,这样就达不到动态分配中断请求的目的。Intel为Pentium设计了一种更为通用的中断控制器,称为“高级可编程中断控制器”(Advanced Programmable Interrupt Controller,APIC)。考虑到“处理器间中断请求”的需要,每个CPU还要有本地的APIC,因为CPU常常要有目标地向系统中的其他CPU发出中断请求。从Pentium开始,Intel在CPU芯片内部集成了本地APIC,但在SMP结构中还需要一个外部的、全局的APIC,形成如下图所示的结构。