1. 程式人生 > >Linux 的 Spinlock 在 MIPS 多核處理器中的設計與實現

Linux 的 Spinlock 在 MIPS 多核處理器中的設計與實現

引言

隨著科技的發展,尤其是在嵌入式領域,高效能、低功耗的處理器成為眾多廠商追逐的目標,但是由於技術和工藝的瓶頸,試圖在單核處理器上達到這樣的目標變得越發困難,於是人們提出了多核處理器的概念。多核處理器的核心思想是一個處理器中包含若干個核(或執行緒),所有核(或執行緒)之間共享 IO、Cache、記憶體等資源,對於這些資源的使用和分配由硬體來完成,使用者無需關注細節,因此每個核(或執行緒)對於使用者來說就好像一個獨立的虛擬 CPU,從使用者角度來看,這個虛擬 CPU 獨佔所有的外設資源。

目前比較流行的多核處理器的架構有下面幾種:

(1)SMP(Symmetric Multi-Processor)

這種架構的處理器由多個核組成,每個核有自己獨立的 Cache,所有核共享記憶體和 IO。

(2)SMT(Symmetric Multi-Thread)

這種架構的處理器的每個核由多個執行緒組成(此處的執行緒指的硬體執行緒,而不是我們所說的作業系統的執行緒概念),每個核下的所有執行緒共享暫存器、ALU(CPU 運算單元)、Cache、記憶體、IO 等資源,執行緒之於使用者也像一個虛擬的 CPU。這種架構的最大優勢在於執行緒和執行緒之間的切換很快,通常一個時鐘週期內就能完成。

(3)NUMA(Non-Uniform Memory Access)

這種架構和前面兩種的區別在於它不是簡單的一個處理器,而是一個由多個處理器組成的系統,每個處理器作為一個結點在該系統中存在。對於記憶體、IO 等資源所有結點也是共享的。

目前比較流行的處理器架構大多都推出了多核處理器的產品,比如 Intel 的 X86 雙核處理器、Freescale 的 PPC 多核處理器、SUN 的 SPARC 多核處理器、RMI 和 Cavium 的 MIPS 多核處理器等。

MIPS 這種架構誕生於 Standford 大學,它的名字含義有雙重意義,一是 “Microcomputer without Inter-locked Pipeline Stages”,另一種是 “Millions of Instructions Per Second”,它設計思想的核心是採用多級指令流水線技術達到高效的處理速度,目前比較常用的 MIPS 採用5級流水線的技術。因其效能高效、結構簡單,MIPS 被譽為 RISC(Reduced Instruction Set Computing) 家族中最優美的一款處理器架構。基於 MIPS 的多核處理器因為其低功耗、高效能從而在嵌入式市場得到了廣泛的應用。

針對上述這些多核處理器的特點,Linux 提出了 Multi-Processing 的概念,它的排程器可以將作業系統的執行緒均分到各個核(或硬體執行緒)上去執行,以此達到平行計算的目的,從而也可以極大地提高系統的效能。

 

Spinlock 的思想

Linux 在推出了 Multi-Processing 之後,多核處理器的並行處理的能力得到了極大的發揮,但是這同時也帶來了一個問題,並行執行勢必就存在多個核同時訪問共享資源的情況,如何能夠保證一個核在訪問共享資源時,該共享資源不會被其它核所改寫呢?

我們沒有方法去控制對共享資源訪問的有序性,但是我們有能力對共享資源採用鎖的保護機制,當某個共享資源被鎖住時,只有獲取該鎖的 CPU 核能夠操作共享資源,其餘試圖訪問共享資源的 CPU 核只能夠等待這個鎖的釋放,這就是在 Linux 中被稱為 Spinlock 的自旋鎖保護機制。

Spinlock 的設計思想是基於一種被稱為 Test-and-Set 的機制,它的操作分為三部分:

(1)INIT

初始化 lock 值。示例如下:

lock := CLEAR;

2)LOCK

這個流程包括兩部分:首先 CPU 核反覆輪詢 lock 值直到它處於空閒狀態為止,然後利用 Test-and-Set 方式嘗試設定該 lock 值,Test-and-Set 的操作可以描述成三步:

(a)讀取 lock 值;

(b)設定 lock 值;

(c)檢查 lock 值是否設定成功,如果在步驟 (a) 之後還存在別的對 lock 值的操作,說明存在有併發訪問 lock 值的情況,則步驟 (b) 的 lock 值將不能設定成功,還需要回到步驟 (a) 重新執行這個流程。

示例如下:

while ((lock == BUSY) || (TestAndSet(lock) == BUSY));

(3)UNLOCK

釋放 lock 值。示例如下:

lock := CLEAR;

 

MIPS 中 Spinlock 的實現

傳統的 C 語言是無法實現 Test-and-Set 的機制的,因為它無法在多核之間建立一個互動的機制,因此 Test-and-Set 需要處理器給以相應的支援。以 MIPS 為例,它提供了 LL(Load Linked Word) 和 SC(Store Conditional Word) 這兩個彙編指令來實現對共享資源的保護。

LL 指令的功能是從記憶體中讀取一個字,以實現接下來的 RMW(Read-Modify-Write) 操作;SC 指令的功能是向記憶體中寫入一個字,以完成前面的 RMW 操作。LL/SC 指令的獨特之處在於,它們不是一個簡單的記憶體讀取/寫入的函式,當使用 LL 指令從記憶體中讀取一個字之後,比如 LL d, off(b),處理器會記住 LL 指令的這次操作(會在 CPU 的暫存器中設定一個不可見的 bit 位),同時 LL 指令讀取的地址 off(b) 也會儲存在處理器的暫存器中。接下來的 SC 指令,比如 SC t, off(b),會檢查上次 LL 指令執行後的 RMW 操作是否是原子操作(即不存在其它對這個地址的操作),如果是原子操作,則 t 的值將會被更新至記憶體中,同時 t 的值也會變為1,表示操作成功;反之,如果 RMW 的操作不是原子操作(即存在其它對這個地址的訪問衝突),則 t 的值不會被更新至記憶體中,且 t 的值也會變為0,表示操作失敗。

SC 指令執行失敗的原因有兩種:

(1)在 LL/SC 操作序列的過程中,發生了一個異常(或中斷),這些異常(或中斷)可能會打亂 RMW 操作的原子性。

(2)在多核處理器中,一個核在進行 RMW 操作時,別的核試圖對同樣的地址也進行操作,這會導致 SC 指令執行的失敗。

採用 LL/SC 指令實現 Spin_Lock/Spin_Unlock 的範例如下。

MIPS 的 Spin_Lock

對於 MIPS 的 Spin_Lock 操作,可以用如下的方式實現:

表1. Spin_Lock
1 Spin_Lock(lockky)
2 again:
3         ll  $t0,lockky
4         beqz $t0,again
5         li  $t0,1
6         sc  $t0,lockky
7         beqz  $t0,again
8         sync    

Line 1:

lockKey 是共享資源鎖,這是塊能被多核所共享的記憶體地址,該值為0表示鎖處於空閒狀態,為1表示鎖已經被某個核所獲取,其餘核若想獲取它只能等待,以下對於 lockKey 的定義相同。

Line 3:

將 lockKey 讀入t0暫存器中。

Line 4:

比較 lockKey 是否空閒,如果該鎖不可用的話則跳轉到 Line 1。

Line 5:

給 t0 暫存器賦值為1。

Line 6:

將 t0 暫存器的值儲存入 lockKey 中,並返回操作結果於 t0 暫存器中。

Line 7:

判斷 t0 暫存器的值是否為0,如果為0表示 Line 5 中的操作失敗,則返回 Line 1 重新開始;如果為1表示 Line 5 中的操作成功。

Line 8:

Sync 是記憶體操作同步指令,用來保證 sync 之前對於記憶體的操作能夠在 sync 之後的指令開始之前完成。

表 2. 實現$s4與$s0指向的值的原子交換
L:
       addi  $t0,$zero,$s4
        ll  $t1,0($s1)
        beq  $t1,$zero,L
        sc  $t0,0($s1)
        beq  $t0,$zero,L
        add  $s4,$zero,$t1

MIPS 的 Spin_Unlock

對於 MIPS 的 Spin_Unlock 操作,可以用如下的方式實現:

表 3. Spin_Unlock
Spin_Unlock(lockkey)
1      sync
2      sw zero, lockkey

Line 2:

給 lockKey 賦值為 0,表示這個鎖被釋放。

 

Spinlock 的不足和改進

Spinlock 在多核處理器中得到了廣泛的運用,但是它也存在一些不足,特別是在對共享資源競爭很激烈的情況下,這種不足會帶來很嚴重的後果。

(1)Spin_Lock 操作是不可重入的,在同一個核上如果在釋放鎖之前呼叫了多次 Spin_Lock,這將導致當前 CPU 核處於死鎖狀態,嚴重情況下會引起系統的崩潰。

(2)Spinlock 不能保證程序對共享資源訪問的公平性和有序性,先申請鎖的程序有可能反而在後申請鎖的程序之後獲取鎖,嚴重時甚至可能導致某個程序長時間無法獲取鎖而處於飢餓狀態。

(3)當 Spinlock 被用於保護共享記憶體時,對於併發讀操作的保護是多餘的,反而會降低系統性能。

為了解決 Spinlock 的上述不足,我們提出了以下的改進措施,有些措施已經在 Linux 中有所體現。

 

 

參考連結:https://www.ibm.com/developerworks/cn/linux/l-cn-spinlock_mips/index.html