
在現代ARM多核處理器系統中,啟動流程是一個復雜而精密的過程,需要協調多個處理核的有序啟動和工作分配。
與單核處理器不同,多核處理器的啟動不僅僅是簡單地執行一段啟動代碼,而是涉及核間的通信、任務分配和同步等復雜機制。
典型的ARM多核啟動流程可以分為以下幾個階段:
當ARM多核處理器上電或復位時,所有處理器核心都會同時從復位向量開始執行。
但系統設計通常會指定一個核心作為主核心(Primary Core,通常是Core0),而其他核心則作為從核心(Secondary Cores)進入等待狀態。
ARM處理器在復位后會從特定的地址(復位向量)開始執行代碼。
對于多核處理器,所有核心最初都執行相同的代碼:
_start:
b reset_handler ; 復位處理
b undef_handler ; 未定義指令處理
b svc_handler ; 軟件中斷處理
b prefetch_handler ; 預取中止處理
b data_handler ; 數據中止處理
nop ; 保留
b irq_handler ; IRQ中斷處理
b fiq_handler ; FIQ中斷處理
每個核心需要通過讀取處理器ID寄存器來確定自己的身份,并決定后續行為:
reset_handler:
mrc p15, 0, r0, c0, c0, 5 ; 讀取多核親和寄存器
ands r0, r0, #0x03 ; 獲取核心ID
beq primary_core_init ; 如果是核心0,跳轉到主核心初始化
b secondary_core_wait ; 其他核心進入等待狀態
主核心負責系統的全局初始化工作:
void primary_core_init(void)
{
/* 1. 關閉中斷 */
disable_interrupts();
/* 2. 初始化關鍵硬件 */
init_clock(); // 時鐘初始化
init_memory(); // 內存控制器初始化
init_uart(); // 串口初始化(用于調試輸出)
/* 3. 設置異常向量表 */
setup_vector_table();
/* 4. 初始化MMU和緩存 */
init_mmu();
enable_cache();
/* 5. 初始化多核通信機制 */
init_smp();
/* 6. 喚醒從核心 */
wakeup_secondary_cores();
/* 7. 跳轉到操作系統加載 */
load_kernel();
}
從核心在上電后會進入低功耗等待狀態,等待主核心的喚醒信號。常見的喚醒機制包括:
/* 在共享內存中定義自旋表 */
volatile uint32_t secondary_spin_table[NR_CPUS];
void secondary_core_wait(int core_id)
{
/* 等待主核心設置跳轉地址 */
while (secondary_spin_table[core_id] == 0) {
wfi(); // 等待中斷,降低功耗
}
/* 獲取跳轉地址并執行 */
uint32_t entry = secondary_spin_table[core_id];
((void (*)(void))entry)();
}
void wakeup_secondary_cores(void)
{
for (int i = 1; i < NR_CPUS; i++) {
secondary_spin_table[i] = (uint32_t)secondary_core_entry;
dsb(); // 數據同步屏障
sev(); // 發送事件信號,喚醒從核心
}
}
當Bootloader完成硬件初始化后,會將控制權轉交給操作系統內核。此時多核處理器的初始化進入操作系統層面。
在Linux內核中,多核啟動流程大致如下:
start_kernel()函數完成系統全局初始化smp_prepare_cpus()準備多核環境smp_init()啟動從核心secondary_start_kernel()開始執行// 主核心執行路徑
asmlinkage __visible void __init start_kernel(void)
{
// ...大量初始化工作...
smp_prepare_cpus();
// ...
smp_init();
}
// 從核心執行路徑
void secondary_start_kernel(void)
{
struct mm_struct *mm = &init_mm;
// 設置異常向量表
set_vector_table();
// 初始化當前核心的MMU
cpu_set_ttbr(swapper_pg_dir);
flush_tlb_all();
// 初始化本地定時器
percpu_timer_setup();
// 通知主核心當前核心已啟動
notify_cpu_starting(smp_processor_id());
// 使能本地中斷
local_irq_enable();
local_fiq_enable();
// 進入CPU空閑循環
cpu_startup_entry(CPUHP_ONLINE);
}
當所有核心都啟動后,操作系統需要合理地將任務分配到各個核心上執行。現代操作系統通常采用以下策略:
// 內核調度器會將任務分配到不同核心
void schedule(void)
{
struct task_struct *prev, *next;
struct rq *rq;
int cpu;
cpu = smp_processor_id();
rq = cpu_rq(cpu);
// 選擇下一個要運行的任務
next = pick_next_task(rq);
if (next != rq->curr)
{
// 執行上下文切換
context_switch(rq, prev, next);
}
}
多核處理器需要高效的核間通信機制,常見的有:
// 發送IPI中斷給指定核心
void send_ipi(int target_cpu, enum ipi_message_type msg)
{
// 設置共享內存中的消息
ipi_data[target_cpu] = msg;
// 內存屏障保證數據可見性
smp_wmb();
// 觸發中斷
gic_raise_softirq(cpumask_of(target_cpu), 1);
}
ARM多核系統通常使用以下機制保證緩存一致性:
多核處理器需要復雜的電源管理策略:
// 核心休眠示例
void cpu_do_idle(void)
{
dsb(); // 確保所有內存訪問完成
wfi(); // 等待中斷
isb(); // 確保流水線刷新
}
調試多核啟動問題可能具有挑戰性,常用方法包括:
核心特定日志:
printk("[CPU%d] %s\n", smp_processor_id(), message);
JTAG調試:
定時分析:
uint64_t get_cycles(void)
{
uint32_t lo, hi;
asm volatile("mrrc p15, 0, %0, %1, c14" : "=r"(lo), "=r"(hi));
return ((uint64_t)hi << 32) | lo;
}
ARM多核處理器的啟動流程是一個從硬件到軟件的多層次協作過程,涉及以下關鍵點:
理解多核啟動流程對于開發高性能、高可靠性的嵌入式系統至關重要。
隨著ARM處理器核心數量的增加(如Neoverse N1支持128核),多核啟動和管理的復雜性也在不斷提高,這需要系統開發者深入理解底層硬件機制和操作系統支持。

覺得文章不錯,點擊“分享”、“贊”、“推薦” 唄!