2. VxWorks SMP内核对象与调度框架

在单核VxWorks中,任务、调度器、就绪队列、互斥量和中断之间的关系相对直观:任一时刻只有一个任务真正占用CPU,调度决策只影响“下一个谁上”。进入SMP(对称多处理)后,同一份内核代码可能同时在多个核上执行,内核对象不再只被一个执行流访问,调度框架也必须回答两个新问题:哪个核来运行这个任务,以及多个核同时操作同一内核对象时如何保证一致性。本节围绕任务、调度器、就绪队列、自旋锁与CPU亲和性,梳理它们在SMP下的语义变化。

一、任务:从“唯一执行体”到“可被多核并发调度”

任务(task)仍是VxWorks调度的基本单位,拥有独立的TCB(任务控制块)、栈、优先级和状态。SMP下任务语义的关键变化在于:任务不再绑定到某个固定核,除非显式设置CPU亲和性。一个就绪任务可以被任意一个空闲核选中执行,也可能在运行过程中因抢占、阻塞或迁移而改变所在核。

任务状态在SMP下依然包括就绪(READY)、阻塞(PEND)、延迟(DELAY)、挂起(SUSPEND)等,但“就绪”的含义扩展为“在某个核的就绪队列中等待被调度”。因此,任务状态迁移必须考虑核间同步:一个核修改任务状态时,其他核可能正在读取该任务的状态或TCB字段,必须通过内核自旋锁保护。

二、调度器:全局调度决策与每核调度执行

VxWorks SMP采用全局调度器语义:所有核共享同一套优先级空间和同一组就绪任务,调度决策基于全局最高优先级就绪任务。但调度动作是分布式的——每个核在自身调度点(如中断返回、任务阻塞、时间片到期)独立触发调度,从全局就绪集合中选择可运行的最高优先级任务。

这意味着调度器需要维护“全局就绪状态”与“每核当前任务”两个层次的信息。全局层决定谁应该运行,每核层决定本核实际运行谁。当某个核释放了一个更高优先级任务时,它需要判断该任务应由哪个核运行:若存在空闲核或可抢占核,则通过核间中断(IPI)通知目标核触发重调度。

核心语义变化:单核调度是“本地决策”,SMP调度是“全局决策 + 核间通知 + 本地执行”。调度器不再只属于一个核,而是被多个核并发访问的共享资源。

三、就绪队列:全局就绪集合与每核就绪视图

就绪队列是调度器最核心的数据结构。SMP下通常采用全局就绪队列加每核就绪位图的组织方式:全局队列按优先级维护所有就绪任务,每核通过位图快速定位本核可运行的最高优先级。这样既保证全局优先级语义一致,又避免每个核都扫描完整任务链表。

就绪队列的插入、删除和查找都必须加自旋锁。典型操作包括:任务从阻塞变为就绪时插入全局队列;任务被选中运行时从就绪队列移除;任务被抢占时重新插入。由于多个核可能同时操作队列,锁的粒度和持有时间直接影响SMP扩展性。

VxWorks SMP 调度框架与内核对象关系 全局就绪队列(按优先级组织) Prio 5 任务A Prio 8 任务B Prio 12 任务C Prio 20 任务D … 就绪队列自旋锁 CPU 0 当前任务:任务A 本地就绪位图 调度点:中断返回/阻塞 CPU 1 当前任务:任务B 本地就绪位图 调度点:时间片到期 CPU 2 当前任务:空闲 本地就绪位图 调度点:IPI 通知 IPI 核间中断 CPU亲和性:任务可绑定到指定核集合,限制迁移范围

四、自旋锁:SMP内核对象的保护机制

自旋锁(spinlock)是SMP下保护短临界区的核心同步原语。与单核下通过关中断保护临界区不同,SMP中一个核关中断并不能阻止其他核访问共享数据,因此必须使用自旋锁。VxWorks提供spinLockIsrTake()、spinLockIsrGive()等接口,在获取锁的同时关闭本核中断,防止中断处理程序与任务上下文竞争同一把锁。

自旋锁的使用原则是:临界区尽可能短,锁内不做阻塞操作,不调用可能引起任务切换的函数。典型保护对象包括就绪队列、任务TCB状态字段、全局调度器数据结构、内核对象引用计数等。锁的粒度需要权衡:粒度过粗会降低多核并行度,粒度过细则增加锁管理开销和死锁风险。

/* SMP下保护就绪队列的典型自旋锁用法 */
spinlockIsr_t  readyQlock;

void readyQInsert(TCB *pTcb)
{
    SPIN_LOCK_ISR_TAKE(&readyQlock);   /* 关本核中断 + 获取自旋锁 */
    /* 将pTcb按优先级插入全局就绪队列 */
    /* 更新本核就绪位图 */
    SPIN_LOCK_ISR_GIVE(&readyQlock);   /* 释放自旋锁 + 恢复中断 */
}

五、CPU亲和性:控制任务在哪些核上运行

CPU亲和性(CPU affinity)用于限定任务可运行的核集合。默认情况下任务不绑定核,由调度器在全局就绪集合中自由分配;通过taskCpuAffinitySet()可将任务绑定到指定核或核集合。亲和性在SMP下有明确的工程价值:

  • 缓存局部性:将频繁访问同一数据的任务绑定到同一核,减少跨核缓存失效。
  • 隔离关键任务:将高实时任务绑定到专用核,避免被其他任务迁移干扰。
  • 中断亲和配合:将设备中断和对应处理任务绑定到同一核,降低核间通信开销。

需要注意,亲和性设置过窄可能导致负载不均:若某核被绑定过多任务,而其他核空闲,全局调度器也无法将任务迁移过去。因此亲和性应作为性能调优手段,而非默认配置。

六、SMP调度框架的协同关系

把上述对象串起来看:任务状态变化触发就绪队列操作,就绪队列由自旋锁保护,调度器在全局就绪集合上做决策,通过IPI通知目标核,目标核在本地调度点切换到新任务,CPU亲和性则约束任务可被哪些核选中。这五者构成SMP调度框架的闭环。

内核对象单核语义SMP语义变化
任务绑定唯一CPU可迁移,受亲和性约束
调度器本地决策全局决策 + 核间通知
就绪队列单一队列全局队列 + 每核位图
互斥保护关中断自旋锁 + 关本核中断
CPU亲和性无意义限制任务可运行核集合
理解SMP调度框架的关键,是区分“全局可见状态”和“每核本地状态”:全局就绪队列、任务状态字段属于全局共享,必须加锁;每核当前任务、本地就绪位图属于核私有,访问时无需跨核同步。混淆这两层,是SMP下出现数据竞争和调度异常的主要根源。

实际工程中,建议先用默认无绑定策略验证功能正确性,再根据性能剖析结果逐步引入亲和性绑定和锁粒度优化。每次调整后,重点观察各核负载是否均衡、高优先级任务唤醒延迟是否稳定、自旋锁争用是否成为瓶颈,以此判断SMP调度配置是否达到预期。