sync

多核與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.
  IMAGE APIC LAYOUT
  (圖中的"IPI"即inter-processor interrupt, 即剛纔提到的"核於核之間的中斷", 圖中的#processor都是一個core)

  向全部AP廣播IPI是很簡單的,只須要操做APIC的64位⁴的ICR寄存器: 往低32位寫入一個double word, IPI就發出去了.
  IMAGE ICR
 
  咱們關心的位段是:
  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.

1.2 從修改bios開始
  上一節末尾,咱們決定對apic編程,讓cpu的每一個AP核執行一段loop代碼,使屏幕上的對應區域的字符不停跳躍,變更。
  你確定以爲,應該在mbr裏寫咱們的代碼。是的,我開始就是這麼作的。像這樣( 這不是我最初寫的那個「版本」,那個「版本」被逐漸修改掉了):
org 0x7c00
[bits 16]

;re-map apic base address to 0x8000
mov ecx, 1bh
rdmsr
and eax, 0xfff
or eax, 0x8000
wrmsr

;copy boot code for ap
;memcpy( ( char *)0x7000, unified_entry, 0xff )
mov ax, 0
mov es, ax
mov ds,ax
mov di, 0x7000
mov si, unified_entry
cld
mov cx, 0xff    ;enough, the boot code size isn't larger than 255 bytes
rep movsb

;send ipi msg using shorhand:all excluding self( 11 )
mov bx,0
mov ds,bx
mov dword [0x8300], 0xc4500        ;INIT IPI
mov dword [0x8300], 0xc4600|7        ;sipi, 7 means 0x7000<<12
jmp $        

unified_entry:        ;boot code for ap
    inc word [ap_count]    
    mov bx,0xb800
    mov gs,bx
    mov bx, [ap_count]
    shl bx, 1
    .spin:
        inc byte [gs:bx]
    jmp .spin    
ap_count: dw 0

jmp $
times 510-($-$$) db 0
dw 0x55aa

  這些代碼你能看個大概,除了開頭一段。那是把APIC寄存器映射到低端內存, 由於它默認是影射在0xFEE00300處,實模式下訪問不了¹。
  可是,當咱們把這個文件彙編,dd到虛擬硬盤,啓動bochs²————屏幕上沒有動靜。
  這真是糟糕,這幾乎是最壞的結果。咱們寧肯bochs崩潰,那至少說明咱們的指令作了什麼。
  如今,怎麼應對就因人而異了:
  咱們的第一反應的大概都是Ctrl+C, info一下cpu,開始思索怎麼調試,但你很快發如今bochs下調試smp不那麼容易,咱們只能info出來bsp的cpu,並且像APIC這種內存映射式的寄存器,用xp命令查不了(那就是怎麼都查不了了 );
  而後大概是google。網上能搜到的資料只有intel文檔.你能夠選擇更細緻的讀它(這是比較考驗心理素質的);
  最後就是去論壇(比較少,我知道的只有osdev)問了,像這種問題,只能是貼代碼問,彷佛有些掃興。這還不是最壞的,最壞的是你在依賴論壇來解決非解決不可的問題,若是你有自學的經歷,你應該知道我在說什麼。
   
  因此,做者選擇從修改bios開始,只是做者選擇的一種途徑。由於我以前知道bios裏有對apic的操做。咱們準備找到它那一部分代碼,先修修改改————咱們急切的想看對APIC乃至AP對咱們的指令,能有一點響應。
  並且有bochs,它的bios代碼是寫在.c和.s文件裏的,咱們直接修改,而後從新編譯bochs就行了。
  能夠選擇用bochs單步調試,但你很快就發現bochs對多核調試的支持很弱,只能info bsp的的cpu.你決定從第一條代碼開始檢查,你試着xp /100 0x8000,想看看APIC寄存器有沒有被映射下來,但輸出的全是0. 你不甘心,

1,能夠訪問,但反而須要對保護模式有更深的瞭解。本文假設讀者是不知道保護模式的。
2,關於smp下bochs的開發環境的配置,參見我另外一篇文章。ios

相關文章
相關標籤/搜索