多核与cache
自己最近在学习smp,顺便写下这些文章,跟大家分享。面向的读者,是对x86硬件和os内核有一定基础的程序员。
第一篇 点着每一个核
1.1 初识APIC
从P6家族的CPU开始,intel引入了初始化多核的硬件机制. cpu上电之后,硬件会自动选择一个核作为BSP(boot-strap processor), 剩余的核作为AP(application processor).注意,取这两个名字,并不是因为这些核在硬件结构上有区别,这些核是一模一样的.只是在初始化阶段,扮演的角色不同,AP几乎²是刚上电就halt住¹,而BSP则会像传统的单核cpu里那样,跳去执行bios代码.
那么,怎样把一段代码交给某个ap执行呢? 这是我才接触多核时,第一关心的问题. 因为知道这一点,就知道怎么写一个多核的操作系统了.
先不看intel是怎么做的,现在假设你是硬件工程师,你会怎么设计?
AP核都已经"睡着"了,只有BSP核在运行我们的代码,所以需要bsp给AP发消息,告诉它去执行哪一段代码. 发消息就是发中断. cpu³通过中断号跳转到某段代码是我们再熟悉不过的了.
intel跟我们设计的大同小异, 为了实现核与核之间的通信,它设计了新的中断控制器,取代旧有的8259A,名字也很形象,就叫andvanced programmable interrupt controller(APIC). 每个核有一个属于自己的apic.
[img=500,303]http://imglf1.ph.126.net/o66p3mMuSpygLYofpuEhPQ==/6630834669236053444.jpeg[/img]
(图中的"IPI"即inter-processor interrupt, 即刚才提到的"核于核之间的中断", 图中的#processor都是一个core)
向所有AP广播IPI是很简单的,只需要操作APIC的64位⁴的ICR寄存器: 往低32位写入一个double word, IPI就发出去了.
[img=500,487]http://imglf0.ph.126.net/-rdCdyhjXTkVyoxL6enwAg==/6630585080096534901.jpeg[/img]
我们关心的位段是:
Destination Shothand:
00 No Shorthand 即禁用shorthand模式,因为有时我们往指定
的core发送IPI,就需要往ICR高32位寄存器的
destination field里填写详细的地址(通常是目
标core的apic id)
01 self
10 all Including self
11 all excluding self 这个是我们需要的
Delivery Mode: 发送什么类型的IPI
000: Fixed 即常规中断,中断号在vector位段里
100: NMI 不可屏蔽中断,会导致硬件重启. vector ignored
101: INIT cause target core perform an INIT. vector must be 0
110: Start Up
Delivery Status: read only, 指示上次IPI的发送状态
0: Idle 发送完成
1: Send Pending 发送未完成
一些不常用的位,我们设置一下就不管它了.
Destination Mode: 0
Level : 1
Trigger Mode: 0
我们再回忆一下我们的构想:我们要给APs广播一个IPI,通过这个IPI携带的中断号,让所有的AP跳去执行某段代码.
就FIX类型的IPI而言, 它的实现跟我们的构想完全一致.但在对AP的初始化上,也就是cpu上电后,APs进入等待状态,怎么让它们由这个状态跳去执行"某段代码"(通常是为他们安排的初始化代码)呢,intel的做了专门的设计,这个设计属于IA32上smp 初始化协议⁵的一部分:
1, 要往AP广播两次IPI,而不是一次.
首先广播一个INIT类型的IPI,然后广播一个start-up类型的IPI.
2, start-up IPI里的vector位段存放的不是中断号,而是(target code address base / 0x1000). intel应该是刻意的避免smp的初始化依赖于实模式的中断机制.⁶
好了, 现在我们可以畅想一下自己的代码了(虽然对APIC的编程还不是很有信心). 我们计划让APs跳去执行这样一段代码⁷:
inc byte [cpu_count]
mov bx, 0xb800
mov ds, bx
l: inc [cpu_count]
jmp l
cpu_count: db 0
预想的结果,是屏幕左上角开始的第2个字符,一直到第(2+AP_count-1)个字符,会同时快速的跳跃. 每个字符的跳跃,对应着一个核的运转.
下一小节见.
---------------------------------------------------------------------
1. 我用halt,只是形容它的状态,不是说它执行了hlt指令.
2. 会完成一个硬件上的minimal self-configuration.
3. 准确说应该是"核", 以后此类的都需要你靠上下文区分.
4, In xAPIC mode the ICR is addressed as two 32-bit registers, ICR_LOW(ffe0 0300H) and ICR_HIGH(FFE0 0310H).
5, Multiprocessor Specification Version 1.4, 所谓协议,应该是跟bios程序员的协议吧~
6, 在hlt模式下能不能直接用FIX IPI做跳转, 目前还没测.
7, nasm语法,以后的汇编器也会使用nasm.