各位好久不见,第五位面壁者回来了!
从今天的这期开始,我将采取视频录播的形式和大家分享我最近学习的一些内容,公众号和B站(账号:第五位面壁者Andrew)同步更新,欢迎各位大佬参观指导。目前暂定的计划是,B站上传录播视频的完整版本,公众号上传视频所用的PPT和视频语音的文字记录,希望这样的分享形式能给大家带来更好的体验,本期内容如下:
https://www.bilibili.com/video/BV1pQ4y1m7pz/
相信能顺着这期标题进来的朋友肯定也对x86多处理器平台的架构和原理比较感兴趣。关于这方面的内容,最权威的资料应该就是英特尔软件开发手册。这份资料很权威,里面的描述也很详细,但是对于新手却不太友好。好在手册在8.4节有提到一本英特尔在1997年发布的多处理器协议V1.4,这本协议定义了Intel多处理器平台的软硬件接口以及相关操作规范,可以帮助我们建立多处理器平台框架的大体概念。我会通过这几期的内容和大家一起学习这本多处理器协议V1.4以及手册中提到的其他关于多处理器管理的知识。
有两点需要大家注意:第一,这里的多处理器特指的是对称多处理器系统,也就是SMP,NUMA系统不在这本spec讨论的范畴之内;第二,从手册第八章的内容来看,这里processor与大家日常工作里说的core,以及支持超线程处理器中的logical processor,对于多处理器协议V1.4来说,这三者的概念是类似的。三者具体的差异,大家可以参考手册中8.9.1和8.9.3中的相关内容。
Spec在开篇中介绍道这份协议的目标是在保证后向兼容性的基础上,建立多处理器平台接口标准。大家仔细品这句话,这里的后向兼容,是谁兼容谁?当然硬件去兼容操作系统和操作系统底下的那一大堆应用程序;平台接口又主要谁用呢,BIOS当仁不让,因为OS不直接和硬件打交道。那在这样的目标之下呢,就演变成了如左图所示的这个大框架,HW建立好了多处理器平台,BIOS会借助HW提供的接口建立一个可以描述MP框架的数据结构,然后再把这个数据结构出传递给OS。这样的好处是什么呢,就是虽然在硬件上增加了处理器的个数,但是上层软件不需要做特殊的改动进行适应,这样就是在保证兼容性的同时提高性能。
在第二章的系统综述中,提出了所谓对称性的概念。这代表着所有的processor在功能上是完全相同的且处于彼此平等的状态,彼此之前也可以相互通信。这种对称性细数下来又分为两个方面:一个是Memory对称性,所有的processor都分享同一个memory space,从实现的角度上说就是所有的处理器共用一套系统总线和内存交互数据;还有一个是IO对称性,所有的processor都共享一个IO子系统,所有的处理器共用一套系统总线和外设交互数据,并且每个处理器可以处理器来自任意外设的任意中断。
下面我们来看一下,要实现这种对称性,硬件需要具体做哪些改动:
首先是处理器方面,虽然所有处理器在功能上是完全相同的,但是还是要将所有的处理器分为BSP和AP。这种划分只在开机初始化和关机过程中有效。其中,BSP负责初始化系统和Boot OS,AP在OS运行起来说才会被激活。至于BSP的选择策略,Spec并没有做强制规定,可以由硬件指定,也可以硬件配合BIOS指定。我们会在最后通过P6微架构进行多处理器初始化的过程来举例说明上述内容。
此外为了实现IO对称性中任意处理器接收任意中断的要求,引入了APIC进行多处理器平台的中断管理。整个APIC分为Local APIC和IO APIC两个部分,其中,IO APIC负责接收外部中断,并随着版本不同通过APIC 、ICC或者SystemBUS发送到Local APIC,随后,Local APIC负责将IO APIC送来的中断消息送到本地CPU。与此同时,Local APIC还要负责传送IPI,用于实现处理器之间的通信。
对于System Memory来说,处理器个数的增多,必然会加大FSB的负担,为了节约FSB的带宽,减少多个处理器不停访问系统内存的开销,加入了L2 Cache,一般来说是若干个处理器共享。这样,大部分针对系统内存的访问转换成了处理器和L2 Cache之间的通信,FSB的带宽得到了部分的释放。也有一些文献讲处理器和L2 Cache之间的总线叫做后端总线。
对于BIOS来说,Spec规定由BIOS负责将处理器和系统的其他部分初始化到一个已知的状态,并且要负责创建包含MP Configurationd的数据结构,并把这个数据结构传递给OS。
对于OS来说,需要做的事情就很少了,毕竟本来就要保证OS的后向兼容性。
第三章的内容是关于MP的硬件Spec,里面的涉及内容比较多,我们就拣比较重要的和大家提一下,具体细节大家可以自行查阅英特尔手册相关章节。
先是系统内存地址空间的分配,整个4G的内存地址空间的分布如图。首先为了兼容,1M以下的实模式地址排布不变,然后4G顶端往下的一部分分配给了BIOS boot,另外的大头就是实际落在内存条上的和MMIO。有所不同的是,为APIC划分了一段区域,但要注意的是,所有的Local APIC 共享同一段区域,每个IO APIC有各自的区域。这两者有什么区别,我们会在后面将APIC的时候举例和大家说明。
这一页把HW spec中的好多内容都放在了一页集中讲,不是说他们不重要,而是他们每一个都很重要,随便拉出来一个个把小时都不一定能说清楚,远超出了今天我们话题的范围,所以我们在这里就大致说说,有兴趣的朋友可以再去相关资料学习。刚才我们看到了内存的分布,之前我们也提过,MP平台具有Memory对称的特性,因此每个处理器都有可能在任意时间对处于内存系统中的某个位置进行访问,同样的一段内存地址,处理器A和B可能同时访问,比如一个要去读,而另一个要去写,先读后写和先写后读,这结果肯定不一样啊,我们怎么来保持多处理器访问内存的可靠性和安全性。MP协议在这里只是提出了要求,但是并没有给出解决方案。比如说,Spec要求MP HW需要保证cache内存的数据一致性,某个地址在每个处理器cache里的数据拷贝应该是一直保持一致的,如果某个拷贝做个改变,那么其他地方也要相应做更新,为了解决这个问题,就诞生了缓存一致性协议,MOSI,MOSIF等;
除此之外,Spec还要求MP访问内存的可靠性,某个P在访问内存的时候不可以受到其他P的干扰,于是又引出来了两个概念:原子操作和Lock机制。这里的原子指代的是不可分割的意思,所谓的原子操作就是某些基本的读取写入内存操作,当某个P进行这种操作的过程中,MP HW保证这一类操作不会受到其他P的影响,比如说奔腾3宣称read or write 1byte 是原子操作,那么在PA从地址0出读取一个字节的过程中,如果PB去写入地址0一个字节,那它能把PA的操作打断或者在PA操作的中间把数据写进去吗?显然不应该能,如果写进去了,那么PA读取的就是PB写的数据而不是原来的正确数据了,硬件就需要保证这样的事情不能发生,因此就保证了这个操作的原子性,它是怎么保证的呢,使用lock机制,所谓lock机制,顾名思义,就是一把锁,把内存系统总线给锁住,在进行原子操作的过程中,其他处理器发来的内存请求就会被暂时blokc住;这种是硬件锁,还有软件锁,因为有些操作肯定不是原子的,但是我们希望他不被打断,比如说RMW中,HW提供lock指令保证在进行这些操作的时候,lock机制生效,其他P的内存操作不会干扰。
然后就是一个memory order的问题,说白了就是处理器访问内存时候的顺序,在P6之前的处理器只能按照程序顺序访问内存,手册管这个叫做program order或者strong order,但是从P6开始,为了提高性能,陆陆续续引入了分支预测和乱序执行等新特性,处理器访问内存可能被reorder,手册称为processor order,MP协议为了保证数据一致性在这里就是对里面的processor order做了一些要求,具体怎么要求大家可以参考Spec有关章节;
当然了,和lock一样,intel其实也保留有软件机制可以让程序员控制某段程序里处理器对内存的访问顺序,业界管这个叫做内存屏障,大家有兴趣可以看看。这一页就蜻蜓点水般的说这么多,大家需要把相关的背景知识补充完了再看这页的内容就很简单了,我在这里也就不班门弄斧了。
然后我们来看第三章的6小节:多处理器中断控制,我觉得这节对我们理解x86中断体系特别实用,值得认真看看。大家可能都学习过PIC 8259和APIC,也都知道,PIC用于单核场景,APIC用于多核场景,但是二者又如何共存又是如何切换的呢,MP Spec里的这一章讲的就是这个问题。这里需要注意一段,这里虽然讲的是APIC,但是对于后续的xAPIC和x2APIC其实都是适用的,xAPIC相对于APIC来说,只是Local APIC和IO APIC之间的通信从APIC BUS升级到了System BUS,而x2APIC的APID和xAPIC的区别只是让APID register的位宽变宽了,整体架构上是没有太大的变化的。好我们回到主题,之前在系统综述里面我们就提过,MP 要保持兼容性,这其中也包括了对只能运行在单核平台的软件的兼容性,所以我们不能直接把PIC丢开,所以MP系统是同时存在PIC和APIC的,为了处理好二者共存和切换问题,MP定义了三种不同的中断模式:PIC Mode、Virtual Wire Mode、对称IO Mode,总的来说,HW需要负责实现PIC or Virual Mode来兼容单核应用,实现对称IO mode应对多核应用,并且保留简单的手法可以进行切换,然后BIOS在boot过程中使用PIC or Virual Wire Mode,在boot完成进入OS前由BIOS或者由OS 进行切换到对称IO Mode。
先看PIC mode,可以看到这个是最简单粗暴的一种共存形式,PIC和APIC各实现了一份,其中PIC的INTR仍旧和BSP的INT信号连接,然后可以通过一个叫做IMCR的寄存器讲整个系统从PIC Mode切换到对称IO Mode。需要注意的是,如果系统没有实现PIC mode,IMCR可能也不会存在。之前我们在综述环节提过,在MP里,BIOS需要根据将MP平台的信息创建一个数据结构传递给OS,这个IMCR是否有实现也包含在这个数据解构中,后续我们再讲到这部分的时候会再提一下。
然后就是Virtual Wire Mode ,这里的virtul wire说的是PIC的INTR那条线的实现方式,在PIC mod中是直接连到了CPU的INT引脚上,在virtul wire mode里,而是通过某种方式连接到了local apic的上,APIC的数据通路在这里代替之前的连接为PIC传递中断信息,所以就是virtual wire,有两种实现形式,先是这种直接bypass IO APIC的,INT连接到了LAPIC的LINT0上,然后NMI连接到了LAPC的LINT1上;另一种是不绕开IO APIC,INTR连接到IOAPIC上,通过IOAPIC和LAPIC的数据通路传递中断消息,当然了NMI还是连接到LINT1上。Spec并没有规定HW应该使用哪种Virtual Wire Mode的实现方式,也没有规定如何从Virtual Mode切换到对称IO Mode。
最后一个是对称IO Mode,这个就是典型的APIC 分离式架构了,其工作原理和方式由很多文献专门介绍,我在这就不赘述了。
还有一个需要注意的就是 我们如何访问Local APIC和IO PI,Spec规定了要将这二者实现成MMIO Devcie。既然是是一个MMIO Devcie,根据MP平台的对称性,所有的处理器共享系统内所有的IO和Memory资源,而Local APIC应该算是CPU的一部分,所以所有处理器的Local APIC的Register都应该映射到同一段地址;而IO APIC是系统的IO Device,不用遵循对称性原则,那么不同的IO APIC应该映射到不同的内存端口上。下面我们通过业界良心RWEverying来实操感受一下这种区别。
最后,Spec还为了兼容性考虑,规定连接到IO APIC上的中断在系统处于PIC和Virtual Wire Mode的时候,也要可以连接到8259上,这样就可以保证,无论在哪种工作模式下,系统中所有的中断都可以得到相应和处理。
受限于篇幅,关于Intel MutiProcesspr Specification V1.4我们今天只分享到这里,我们会在随后的几期视频里分享这本Spec的剩余内容