在 Linux 内核(以及其他 x86 操作系统)的启动术语中,BSP和AP是用来区分多核处理器(SMP)中不同 CPU 核心在启动时角色的标准术语。
BSP (Bootstrap Processor,引导处理器)
角色:它是系统的“主大脑”,是唯一一个在系统上电或复位后,由硬件自动唤醒并开始执行 BIOS/内核代码的 CPU 核心。
职责:
执行所有初始化:BSP 负责执行绝大部分的内核初始化工作,包括内存管理、设备驱动加载、调度器初始化等。
引导系统:它负责运行我们之前讨论的整个启动流程:从实模式到保护模式,再到长模式,最终进入
start_kernel。唤醒 AP:在完成自身初始化后,BSP 会通过特殊的“启动间中断(IPI)”信号,逐一唤醒其他所有 CPU 核心(即 AP)。
AP (Application Processor,应用处理器)
角色:指的是系统中除 BSP 以外的所有其他 CPU 核心。在系统上电时,它们最初是处于休眠或等待状态的。
职责:
执行初始化副本:被 BSP 唤醒后,每个 AP 都会执行一个与 BSP 不同的、简化的初始化路径(通常从
secondary_startup_64开始)。加入系统:它们会完成自身的堆栈设置、缓存初始化等,然后进入等待队列,准备为系统提供计算能力。
它们在启动流程中的位置
BSP:从
arch/x86/kernel/head_64.S中的startup_64入口开始,一路执行到start_kernel。AP:通过
startup_64或secondary_startup_64入口被唤醒,但在执行到我们之前分析的释放trampoline_lock的代码后,它们会跳过 BSP 执行的大部分通用初始化,直接进入各自的空闲循环,准备执行调度器分配的任务。
区分 BSP 和 AP 对于理解内核的启动机制至关重要。BSP 是“发起者”,而 AP 是“追随者”,这种设计确保了多核系统能够有序且高效地完成初始化。
BSP是如何一步步唤醒AP,并让它们最终加入系统的?
整个过程主要由BSP在arch/x86/kernel/smpboot.c中的native_cpu_up()函数驱动。
BSP的“四步舞曲”:唤醒AP的流程
BSP通过发送特殊的处理器间中断(IPI)来唤醒AP,整个过程分为清晰的四个阶段:
发送INIT和SIPI信号:这是唤醒的“敲门”动作。
BSP首先向目标AP发送一个INIT(初始化)IPI,让AP从休眠状态复位,进入等待状态。
接着,BSP会发送一个或两个SIPI(启动IPI)信号。这个信号里包含了一个关键信息:AP应该从哪里开始执行它的第一条指令——这个地址通常指向内存中的
trampoline(跳板)代码。
等待AP“签到”:发送信号后,BSP需要等待AP响应。
AP在收到SIPI后,会执行
trampoline代码,进入保护模式,然后在内核的初始化过程中设置好自己对应的cpu_initialized_mask位,相当于对BSP喊了一声“我收到了!”。
等待AP完成基础初始化:BSP确认AP“签到”后,会等待它完成自身核心的初始化。
AP接着会进行CPU核心的初始化(
cpu_init()),完成后会设置cpu_callin_mask位。这正是之前分析的那段释放
trampoline_lock锁的代码所处的阶段。每个AP必须依次通过这个“锁”,确保对trampoline代码的独占访问,防止多核同时启动时的数据竞争。
最终同步并标记上线:最后一步是将AP完全纳入系统。
BSP会和AP进行TSC(时间戳计数器)同步,确保它们的时间基准一致。
当所有准备就绪,AP会设置
cpu_online_mask位,正式宣告自己“上线”,成为系统可调度任务的一份子。
AP的“觉醒之路”:执行简化的初始化
当AP被唤醒并执行trampoline代码后,它会进入64位模式,并跳转到内核为AP准备的专门入口:secondary_startup_64。这是AP与BSP在代码层面的分水岭。
secondary_startup_64会执行一个比BSP启动过程简化得多的初始化序列:
验证CPU:检查CPU是否支持所需特性。
加载自己的栈:这正是之前分析的那段代码的任务,AP会从
current_task中获取并切换到自己的内核栈。释放
trampoline_lock:如果当前是AP,它会释放这个锁,好让下一个AP能开始启动。跳转到C代码:最终,AP会直接跳转到C语言函数
start_secondary(),开始执行更高级的初始化工作。
在start_secondary()中,AP会完成如设置per-CPU变量、初始化中断控制器等最终步骤,然后进入空闲状态,等待调度器分配任务。