前言
最近報名參加了恩智浦社區(qū)的 LPC55S69 開發(fā)板測評活動,由于其搭載的是一顆 Cortex-M33 Dual Core 的 CPU,而且有大佬已經(jīng)支持了 RT-Thread 的 BSP,就考慮使其支持 RT-Thread 框架下的 SMP,最近就一直在研究 SMP,并在 Raspberry-Pico 上做了一些實驗。以下是一些我在學習過程中的心得體會,不對的地方歡迎大家指正交流~
SMP 簡介
SMP: 對稱多處理(Symmetrical Multi-Processing)簡稱 SMP,是指在一個計算機上匯集了一組處理器 (多 CPU), 各 CPU 之間共享內(nèi)存子系統(tǒng)以及總線結(jié)構(gòu)。雖然同時使用多個CPU,但是從管理的角度來看,它們的表現(xiàn)就像一臺單機一樣。系統(tǒng)將任務(wù)隊列對稱地分布于多個CPU之上,從而極大地提高了整個系統(tǒng)的數(shù)據(jù)處理能力。RT-Thread 自 v4.0.0 版本開始支持 SMP,在對稱多核上可以通過使能 RT_USING_SMP 來開啟。
系統(tǒng)上電后,各 CPU 的啟動流程如下圖所示:
每個次級 CPU 自身硬件部分的初始化不能由 CPU0 完成,因為其自身硬件不能由其它 CPU 訪問。
關(guān)于 CPU 已經(jīng)支持了 SMP 的平臺
RT-Thread 的 libcpu 中有一些芯片類型已經(jīng)支持了 SMP 功能,例如 Cortex-A 系列。對于這樣的平臺,SMP 的移植工作就會簡單很多,我們只需要實現(xiàn) rt_hw_secondary_cpu_up() ,secondary_cpu_c_start() ,rt_hw_secondary_cpu_idle_exec() 這三個函數(shù)即可,具體的移植介紹可以參考 RT-Thread 文檔中心SMP 介紹與移植
關(guān)于 CPU 還未支持 SMP 的平臺
RT-Thread 中還有一些 CPU 是沒有支持 SMP 的,例如 Cortex-M 系列的大部分 CPU,練手的 PICO 是 M0 ,準備開發(fā)的 LPC55S69 是 M33,都是還沒有支持 SMP 的。對于這樣的平臺移植 SMP 就會相對麻煩。除了 rt_hw_secondary_cpu_up() ,secondary_cpu_c_start() ,rt_hw_secondary_cpu_idle_exec() 這三個函數(shù),我們還需要補充 SMP 所需要的底層支持,主要是中斷和調(diào)度部分,從而實現(xiàn)更加復雜的共享資源保護,以及線程間的通訊和調(diào)度。
推薦大家先去看看 RT-Thread 文檔中心的相關(guān)資料,以及這篇文章:[RT-Thread學習筆記] 中斷鎖、調(diào)度鎖與死鎖,最好再去看看 rt-threadsrc 目錄下的 scheduler.c 源碼。其中有許多 SMP 的相關(guān)實現(xiàn)。
CPU ID
scheduler.c 中已經(jīng)有 SMP 的相關(guān)支持,但是會發(fā)現(xiàn),還需要 CPU 的個數(shù)及其對應(yīng)的 ID 等重要參數(shù)。所以我們首先要是實現(xiàn)的是 rt_hw_cpu_id() 這個函數(shù)。這個需要對應(yīng)自己開發(fā)平臺的實際情況。
OS Tick
在 SMP 系統(tǒng)中,每個 CPU 維護自己獨立的 tick 值,用作任務(wù)運行計時以及時間片統(tǒng)計。除此之外,CPU0 還通過 tick 計數(shù)來更新系統(tǒng)時間,并提供系統(tǒng)定時器的功能,次級 CPU 不需要提供這些功能。這部分也是要針對使用的開發(fā)開發(fā)平臺進行配置。
處理器間中斷 IPI
處理器間中斷(Inter-Processor Interrupt)負責 CPU 之間的相互通訊及處理,針對不同開發(fā)的平臺,我們還需要實現(xiàn)以下函數(shù):
/* 該函數(shù)用來向 CPU 位圖中表示的 CPU 集合發(fā)送指定編號的 IPI 信號 /
void rt_hw_ipi_send(int ipi_vector, unsigned int cpu_mask)
/ 函數(shù)為當前 CPU 設(shè)置指定編號 IPI 信號的處理函數(shù) */
void rt_hw_ipi_handler_install(int ipi_vector, rt_isr_handler_t ipi_isr_handler)
調(diào)度與同步
臨界區(qū)保護是需要特別注意的。對于 SMP 通過關(guān)中斷的方式并不能阻止多個 CPU 對共享資源的并發(fā)訪問,需要通過自旋鎖機制進行保護(在次級 CPU 啟動中就需要使用)。我們就還需要實現(xiàn)以下幾個函數(shù):
rt_hw_spin_lock_init() /* 初始化已分配的 spinlock 變量 /
rt_hw_spin_lock() / 獲取 spinlock,忙等待直到獲取成功 /
rt_hw_spin_unlock() / 釋放 spinlock */
需要定義自旋鎖:
typedef union {
unsigned long slock;
struct __arch_tickets {
unsigned short owner;
unsigned short next;
} tickets;
} rt_hw_spinlock_t;
不使用 SMP 的時候,在進行調(diào)度等需要臨界資源保護的情況下,是通過 rt_hw_interrupt_disable/enable 來屏蔽中斷進行保護,但是在 SMP 中,對 rt_hw_interrupt_disable/enable 進行了替換,從而保證對共享資源訪問的互斥:
/* 在 rthw.h 中 */
#ifdef RT_USING_SMP
#define rt_hw_interrupt_disable rt_hw_local_irq_disable
#define rt_hw_interrupt_enable rt_hw_local_irq_enable
#endif
因為 SMP 使用了多核,調(diào)度和同步的情況相較于單個處理器更加復雜和重要,這部分是移植的重點。需要針對開發(fā)平臺的不同,對以下函數(shù)進行重寫(對于 Cortex-M 內(nèi)核,需要注意輔助上下文切換的 PendSV 也是一種中斷):
/* 實現(xiàn)從當前線程切換到目標線程,在 Cortex-M 內(nèi)核里 rt_hw_context_switch() 和 rt_hw_context_switch_interrupt() 功能一致 /
rt_hw_context_switch_interrupt:
rt_hw_context_switch:
/ 實現(xiàn)沒有來源線程切換到目標線程 /
rt_hw_context_switch_to:
/ PendSV 中斷處理函數(shù),在 Cortex-M 內(nèi)核里 PendSV 輔助完成上下文切換 */
PendSV_Handler:
這部分需要在 context_gcc.S 文件中實現(xiàn)。
移植 SMP 的重點是調(diào)度與同步,大家在移植之前,最好對 RT-Thread 的調(diào)度流程和中斷機制有一定的學習和理解,這部分可以參考 RT-Thread 文檔中心,最好能配合著理解源碼的實現(xiàn)。我對 RT-Thread 框架下的 SMP 目前的理解就是以上這些,歡迎大家交流討論。
-
處理器
+關(guān)注
關(guān)注
68文章
19384瀏覽量
230488 -
SMP
+關(guān)注
關(guān)注
0文章
75瀏覽量
19698 -
定時器
+關(guān)注
關(guān)注
23文章
3254瀏覽量
115074 -
Cortex-M
+關(guān)注
關(guān)注
2文章
229瀏覽量
29788 -
RT-Thread
+關(guān)注
關(guān)注
31文章
1301瀏覽量
40265
發(fā)布評論請先 登錄
相關(guān)推薦
評論