Linux多核SMP系统引导机制深度解析1. SMP系统引导概述在x86体系结构的Linux 2.6.32内核中多处理器(SMP)系统的引导过程遵循特定的初始化协议。系统硬件在加电或复位后会动态选择总线上的一个处理器作为引导处理器(Bootstrap Processor, BSP)其余处理器则处于暂停状态称为应用处理器(Application Processors, APs)。BSP需要完成整个系统的引导和初始化创建多个进程后才能启动所有APs投入运行。这一过程涉及处理器状态管理、中断通信和内存同步等关键技术。2. 引导处理器初始化流程2.1 初始化阶段划分BSP的初始化过程可分为以下几个关键阶段实模式初始化完成基本硬件检测和设置保护模式切换启用分段和分页机制内核初始化执行start_kernel()进行系统全局初始化SMP准备为APs启动做好环境准备2.2 关键函数调用链BSP通过以下函数链完成SMP系统初始化start_kernel() → rest_init() → kernel_init() → smp_init()smp_init()是激活其余处理器的入口点其实现位于init/main.cstatic void __init smp_init(void) { unsigned int cpu; for_each_present_cpu(cpu) { if (num_online_cpus() setup_max_cpus) break; if (!cpu_online(cpu)) cpu_up(cpu); // 启动指定CPU } printk(KERN_INFO Brought up %ld CPUs\n, (long)num_online_cpus()); }3. 应用处理器启动机制3.1 AP启动流程概览BSP通过以下步骤启动APs准备AP运行环境内存、任务等通过IPI中断唤醒APAP执行引导代码完成自身初始化AP通知BSP启动完成投入运行3.2 处理器状态管理内核使用per_cpu(cpu_state, cpu)跟踪每个处理器的状态CPU_UP_PREPAREAP启动准备中CPU_ONLINEAP已上线运行3.3 CPU启动函数链BSP通过以下函数链完成单个AP的启动smp_init() → cpu_up() → native_cpu_up() → do_boot_cpu() → wakeup_secondary_cpu_via_init()native_cpu_up()是架构相关的CPU启动实现int __cpuinit native_cpu_up(unsigned int cpu) { mtrr_save_state(); per_cpu(cpu_state, cpu) CPU_UP_PREPARE; err do_boot_cpu(apicid, cpu); // 核心启动逻辑 while (!cpu_online(cpu)) { // 等待AP启动完成 cpu_relax(); ... } return 0; }4. AP唤醒与初始化细节4.1 引导环境准备do_boot_cpu()为AP准备以下运行环境空闲任务复制init进程创建AP的空闲任务GDT设置保存AP的GDT基地址到early_gdt_descr执行入口设置AP初始化完成后执行的start_secondary函数运行栈配置AP使用的内核栈指针static int __cpuinit do_boot_cpu(int apicid, int cpu) { initial_code (unsigned long)start_secondary; stack_start.sp (void *) c_idle.idle-thread.sp; start_ip setup_trampoline() { memcpy(trampoline_base, trampoline_data, trampoline_end - trampoline_data); return virt_to_phys(trampoline_base); } boot_error wakeup_secondary_cpu_via_init(apicid, start_ip); ... }4.2 IPI中断唤醒机制BSP通过APIC的INIT-SIPI-SIPI序列唤醒AP将引导代码(trampoline.S)复制到低端内存(1MB以下)计算引导代码的物理页地址start_eip通过apic_icr_write发送IPI中断携带start_eip12作为vectorwakeup_secondary_cpu_via_init(int phys_apicid, unsigned long start_eip) { apic_icr_write(APIC_DM_STARTUP | (start_eip 12), phys_apicid); }4.3 AP引导代码执行AP被唤醒后从实模式开始执行trampoline.S中的代码设置运行标记(0xA5A5A5A5)供BSP检测加载临时GDT和IDT启用保护模式跳转到startup_32_smp继续初始化ENTRY(trampoline_data) movl $0xA5A5A5A5, trampoline_data - r_base lidtl boot_idt_descr - r_base lgdtl boot_gdt_descr - r_base lmsw %ax // 进入保护模式 ljmpl $__BOOT_CS, $(startup_32_smp-__PAGE_OFFSET)4.4 保护模式初始化startup_32_smp完成AP的保护模式设置初始化段寄存器启用分页机制(使用swapper_pg_dir页表)设置内核栈跳转到start_secondaryENTRY(startup_32_smp) movl $(__BOOT_DS),%eax movl %eax,%ds ... movl $pa(swapper_pg_dir),%eax movl %eax,%cr3 // 设置页表 movl %cr0,%eax orl $X86_CR0_PG,%eax movl %eax,%cr0 // 启用分页 ... ljmp $__BOOT_CS,$1f 1: lss stack_start,%esp jmp *(initial_code) // 跳转到start_secondary5. AP最终初始化5.1 start_secondary函数AP在start_secondary中完成最后初始化调用cpu_init()初始化本地CPU数据结构通过smp_callin()通知BSP启动完成设置CPU在线状态进入空闲循环参与任务调度static void __cpuinit start_secondary(void *unused) { cpu_init(); preempt_disable(); smp_callin(); // 设置cpu_callin_mask set_cpu_online(smp_processor_id(), true); per_cpu(cpu_state, smp_processor_id()) CPU_ONLINE; cpu_idle(); // 进入空闲任务 }5.2 状态同步机制BSP和AP通过以下位图进行状态同步cpu_callout_maskAP已开始初始化cpu_callin_maskAP已完成基本初始化cpu_online_maskAP已完全在线6. 关键技术点解析6.1 低端内存分配的必要性trampoline_base必须分配在1MB以下的低端内存因为AP启动时处于实模式只能访问1MB地址空间IPI中断只传递8位vectorAP将其左移12位得到物理地址因此引导代码必须位于4K对齐的低端物理地址6.2 处理器间通信机制BSP和AP的通信主要通过IPI中断唤醒AP并传递引导信息共享内存通过特定内存位置交换状态信息原子操作确保状态位图的原子更新6.3 内存一致性保证SMP启动过程中的内存同步措施使用wbinvd指令清空缓存内存屏障确保指令顺序原子操作更新共享状态7. 完整启动流程总结BSP完成自身初始化后通过smp_init()启动APs对每个APBSP准备运行环境并发送IPI中断AP从实模式开始执行逐步进入保护模式AP完成自身初始化后通知BSP投入运行所有AP启动完成后系统进入多核运行状态
Linux SMP系统多核启动机制详解
Linux多核SMP系统引导机制深度解析1. SMP系统引导概述在x86体系结构的Linux 2.6.32内核中多处理器(SMP)系统的引导过程遵循特定的初始化协议。系统硬件在加电或复位后会动态选择总线上的一个处理器作为引导处理器(Bootstrap Processor, BSP)其余处理器则处于暂停状态称为应用处理器(Application Processors, APs)。BSP需要完成整个系统的引导和初始化创建多个进程后才能启动所有APs投入运行。这一过程涉及处理器状态管理、中断通信和内存同步等关键技术。2. 引导处理器初始化流程2.1 初始化阶段划分BSP的初始化过程可分为以下几个关键阶段实模式初始化完成基本硬件检测和设置保护模式切换启用分段和分页机制内核初始化执行start_kernel()进行系统全局初始化SMP准备为APs启动做好环境准备2.2 关键函数调用链BSP通过以下函数链完成SMP系统初始化start_kernel() → rest_init() → kernel_init() → smp_init()smp_init()是激活其余处理器的入口点其实现位于init/main.cstatic void __init smp_init(void) { unsigned int cpu; for_each_present_cpu(cpu) { if (num_online_cpus() setup_max_cpus) break; if (!cpu_online(cpu)) cpu_up(cpu); // 启动指定CPU } printk(KERN_INFO Brought up %ld CPUs\n, (long)num_online_cpus()); }3. 应用处理器启动机制3.1 AP启动流程概览BSP通过以下步骤启动APs准备AP运行环境内存、任务等通过IPI中断唤醒APAP执行引导代码完成自身初始化AP通知BSP启动完成投入运行3.2 处理器状态管理内核使用per_cpu(cpu_state, cpu)跟踪每个处理器的状态CPU_UP_PREPAREAP启动准备中CPU_ONLINEAP已上线运行3.3 CPU启动函数链BSP通过以下函数链完成单个AP的启动smp_init() → cpu_up() → native_cpu_up() → do_boot_cpu() → wakeup_secondary_cpu_via_init()native_cpu_up()是架构相关的CPU启动实现int __cpuinit native_cpu_up(unsigned int cpu) { mtrr_save_state(); per_cpu(cpu_state, cpu) CPU_UP_PREPARE; err do_boot_cpu(apicid, cpu); // 核心启动逻辑 while (!cpu_online(cpu)) { // 等待AP启动完成 cpu_relax(); ... } return 0; }4. AP唤醒与初始化细节4.1 引导环境准备do_boot_cpu()为AP准备以下运行环境空闲任务复制init进程创建AP的空闲任务GDT设置保存AP的GDT基地址到early_gdt_descr执行入口设置AP初始化完成后执行的start_secondary函数运行栈配置AP使用的内核栈指针static int __cpuinit do_boot_cpu(int apicid, int cpu) { initial_code (unsigned long)start_secondary; stack_start.sp (void *) c_idle.idle-thread.sp; start_ip setup_trampoline() { memcpy(trampoline_base, trampoline_data, trampoline_end - trampoline_data); return virt_to_phys(trampoline_base); } boot_error wakeup_secondary_cpu_via_init(apicid, start_ip); ... }4.2 IPI中断唤醒机制BSP通过APIC的INIT-SIPI-SIPI序列唤醒AP将引导代码(trampoline.S)复制到低端内存(1MB以下)计算引导代码的物理页地址start_eip通过apic_icr_write发送IPI中断携带start_eip12作为vectorwakeup_secondary_cpu_via_init(int phys_apicid, unsigned long start_eip) { apic_icr_write(APIC_DM_STARTUP | (start_eip 12), phys_apicid); }4.3 AP引导代码执行AP被唤醒后从实模式开始执行trampoline.S中的代码设置运行标记(0xA5A5A5A5)供BSP检测加载临时GDT和IDT启用保护模式跳转到startup_32_smp继续初始化ENTRY(trampoline_data) movl $0xA5A5A5A5, trampoline_data - r_base lidtl boot_idt_descr - r_base lgdtl boot_gdt_descr - r_base lmsw %ax // 进入保护模式 ljmpl $__BOOT_CS, $(startup_32_smp-__PAGE_OFFSET)4.4 保护模式初始化startup_32_smp完成AP的保护模式设置初始化段寄存器启用分页机制(使用swapper_pg_dir页表)设置内核栈跳转到start_secondaryENTRY(startup_32_smp) movl $(__BOOT_DS),%eax movl %eax,%ds ... movl $pa(swapper_pg_dir),%eax movl %eax,%cr3 // 设置页表 movl %cr0,%eax orl $X86_CR0_PG,%eax movl %eax,%cr0 // 启用分页 ... ljmp $__BOOT_CS,$1f 1: lss stack_start,%esp jmp *(initial_code) // 跳转到start_secondary5. AP最终初始化5.1 start_secondary函数AP在start_secondary中完成最后初始化调用cpu_init()初始化本地CPU数据结构通过smp_callin()通知BSP启动完成设置CPU在线状态进入空闲循环参与任务调度static void __cpuinit start_secondary(void *unused) { cpu_init(); preempt_disable(); smp_callin(); // 设置cpu_callin_mask set_cpu_online(smp_processor_id(), true); per_cpu(cpu_state, smp_processor_id()) CPU_ONLINE; cpu_idle(); // 进入空闲任务 }5.2 状态同步机制BSP和AP通过以下位图进行状态同步cpu_callout_maskAP已开始初始化cpu_callin_maskAP已完成基本初始化cpu_online_maskAP已完全在线6. 关键技术点解析6.1 低端内存分配的必要性trampoline_base必须分配在1MB以下的低端内存因为AP启动时处于实模式只能访问1MB地址空间IPI中断只传递8位vectorAP将其左移12位得到物理地址因此引导代码必须位于4K对齐的低端物理地址6.2 处理器间通信机制BSP和AP的通信主要通过IPI中断唤醒AP并传递引导信息共享内存通过特定内存位置交换状态信息原子操作确保状态位图的原子更新6.3 内存一致性保证SMP启动过程中的内存同步措施使用wbinvd指令清空缓存内存屏障确保指令顺序原子操作更新共享状态7. 完整启动流程总结BSP完成自身初始化后通过smp_init()启动APs对每个APBSP准备运行环境并发送IPI中断AP从实模式开始执行逐步进入保护模式AP完成自身初始化后通知BSP投入运行所有AP启动完成后系统进入多核运行状态