1、VxWorks SMP 多核编程指南本文摘自 vxworks_kernel_programmers_guide_6.8 第 24 章1. 介绍VxWorks SMP 是风河公司为 VxWorks 设计的 symmetric multiprocessing(SMP)系统。它与风河公司的 uniporcessor(UP)系统一样,具备实时操作系统的特性。本章节介绍了风河 VxWorks SMP 系统的特点。介绍了 VxWorks SMP 的配置过程、它与 UP 编程的区别,还有就是如何将 UP 代码移植为 SMP 代码。2. 关于 VxWorks SMP多核系统指的是一个系统中包含两个或两个以上的
2、处理单元。SMP 是多核技巧中的一个,它的主要特点是一个 OS 运行在多个处理单元上,并且内存是共享的。另一种多核技巧是 asymmetric multiprocessing(AMP )系统,即多个处理单元上运行多个 OS。(1) 技术特点关于 CPU 与处理器的概念在很多计算机相关书籍里有所介绍。但是,在此我们仍要对这二者在 SMP 系统中的区别进行详细说明。CPU:一个 CPU 通常使用 CPU ID、物理 CPU 索引、逻辑 CPU 索引进行标示。一个CPU ID 通常由系统固件和硬件决定。物理 CPU 索引从 0 开始,系统从 CPU0 开始启动,随着 CPU 个数的增加,物理 CPU
3、 索引也会增加。逻辑 CPU 索引指的是 OS 实例。例如,UP 系统中逻辑 CPU 的索引永远是 0;对于一个 4 个 CPU 的 SMP 系统而言,它的 CPU 逻辑索引永远是 0 到 3,无论硬件系统中 CPU 的个数。处理器(processor) :是一个包含一个 CPU 或多个 CPU 的硅晶体单元。多处理器(multiprocessor):在一个独立的硬件环境中包含两个以上的处理器。单核处理器(uniprocessor):一个包含了一个 CPU 的硅晶体单元。例如:a dual-core MPC8641D 指的是一个处理器上有两个 CPU;a quad-core Broadcom
4、1480 指的是一个处理器上有四个 CPU。在 SMP 系统上运行 UP 代码总会遇到问题,即使将 UP 代码进行了更新,也很难保证代码很好的利用了 SMP 系统的特性。对于在 SMP 上运行的代码,我们分为两个级别:SMP-ready:虽然可以正常的运行在 SMP 系统上,但是并没有很充分的利用 SMP 系统的特点,即没有利用到多核处理器的优势;SMP-optimized:不仅可以正常的运行在 SMP 系统上,而且还能很好的利用 SMP 系统的特点,使用多个 CPU 使多个任务可以同时执行,提高系统的效率,比 UP 系统的效果更加明显。(2) VxWorks SMP OS 特点VxWorks
5、 单核编程( UP)与 SMP 编程在多数情况下是一样的。类似的,多数 API 在UP 和 SMP 编程中是通用的。一些少数 UP 编程中的 API 不能在 SMP 中使用。与此同时,SMP 中的一些 API 在 UP 中使用时表现的不是 SMP 中的效果,而是默认 UP 的效果,或者压根就不能使用(例如,task spinlock 默认表现为 task lock) 。本小节将简短介绍一下 VxWorks 的对称多处理器的一些特点:多任务:对于传统的 UP 系统而言,处理多任务的方法是通过任务优先级对 CPU 资源进行抢占式处理的。而 SMP 系统则改变了这种方法,它是实实在在的任务、中断的同
6、时执行。实现同时执行的关键是多个任务可以在不同的 CPU 上执行,当然这需要 OS 的协调控制。对于 UP 系统中多任务所谓的同时执行,其实只不过是 CPU 的快速切换,占有 CPU的任务由一个快速切换到另一个。在 SMP 系统中,同时执行不是幻想而是实实在在存在的。任务调度机制:VxWorks SMP 系统中的任务调度机制与 UP 中的类似,都是基于优先级的。不同的是,当不同的任务运行在不同的 CPU 上时,可以实现两个任务的同时执行。互斥:由于 SMP 系统允许任务同时运行的情况存在,因此,在 UP 系统中通过关中断、锁任务调度等这些保护临界资源的手段在 SMP 系统中将不再适用。这种在所
7、有 CPU 上通过强行关闭中断、锁任务调度的方法会影响到 SMP 系统发挥它的特点,将 SMP 系统带回到 UP 系统的模式。VxWorks SMP 提供一套特殊的任务间、中断间同步/互斥的方法即 UP 中的 taskLock()和 intLock()等将会被 VxWorks SMP 提供的 spinlock,原子操作以及CPU-specific 等机制替代。CPU-Affinity:默认情况下,任意任务可以运行在任意 CPU 上。VxWorks SMP 提供了一种叫做 CPU-Affinity 的机制,即可以分配任务到指定 CPU(CPU 逻辑索引)上执行。(3) VxWorks SMP 硬
8、件特点VxWorks SMP 系统要求硬件必须具备对称多处理器。这些处理器必须是一样的,处理器可以共享内存、可以平等的访问所有设备。VxWorks SMP 必须遵循 uniform memory access (UMA)结构。图 1 显示了一个双 CPU 的 SMP 系统图 1 SMP 硬件结构无论 SMP 系统中 CPU 的个数是多少,它们的重要特点是一样的:a. 内存对所有 CPU 可见,不存在“只属于某个 CPU 的内存”的情况。即任意 CPU 可以在任意内存中执行代码;b. 每个 CPU 都有 Memory Management Unit(MMU)。MMU 可以使任务在不同的虚拟内存中
9、同时运行。例如,RTP1 的一个任务可以在 CPU0 上运行,与此同时, RTP2 的一个任务可以在 CPU1 上运行;c. 每个 CPU 可以访问所有设备。设备产生的中断可以通过可编程中断控制器发送到任意CPU 上执行;d. 通过多 CPU,任务和 ISR 可以实现同步;通过 spinlock,任务和 ISR 可以实现互斥;e. Snoop bus 的作用是使 CPU 之间的 data cache 总是保持前后一致性。(4) VxWorks SMP 与 AMP 的对比关于 SMP 与 AMP 系统中对内存访问的对比如图 2 所示:图 2 SMP 系统对内存的占用情况在 SMP 系统中,所有物
10、理内存被所有 CPU 共享。内存空间可以用来保存 VxWorks SMP 镜像、Real-Time Process(RTP)等。所有 CPU 可以读、写、运行所有内存。内核任务、用户任务可以在任意 CPU 中执行。在 SMP 系统中,所有内存、设备被所有 CPU 共享,CPU 之间的主要通讯是如何防止“同时访问共享资源”的情况发生。图 3 AMP 系统对内存的占用情况在 AMP 系统中,每个 CPU 对应一个 VxWorks 镜像的拷贝,它们只能被对应的 CPU访问。因此,CPU1 中执行的内核任务不可能在 CPU0 的内存中执行,反之亦然。对于RTP 也是一样的。在 AMP 系统中,一些内存
11、是共享的,但是在这些共享内存中读写数据是严格受到控制的。例如,在两个 VxWorks 镜像中传递数据等。硬件资源根据 OS 被划分,因此 CPU之间的通信只有在访问共享内存时才会发生。3. VxWorks SMP 配置说明Spinlock的调式版本组件INCLUDE_SPINLOCK_DEBUG 提供了 spinlock 的版本,这对调试 SMP APP 有帮助。在包含 INCLUDE_SPINLOCK_DEBUG 的同时,最好要加入 INCLUDE_EDR_ERRLOG 组件,它可以记录 spinlock 的错误信息。CPU配置参数组件INCLUDE_KERNEL 组件中包含了一些对 VxW
12、orks SMP 参数的配置,包括:VX_SMP_NUM_CPUS 代表 VxWorks SMP 的使能 CPU 个数。所有体系结构的最大使能 CPU 个数如下: ARM=4, IA32=8,MIPS=32 ,PowerPC=8,VxWorks Simulator=32。ENABLE_ALL_CPUS 默认是 TRUE,代表所有已配置的 CPU 使能。这个参数也可以设置为 FALSE,一般出于调试目的,此时只有逻辑 CPU0 是使能的,只有通过kernelCpuEnable()才可以使能指定的 CPU。VX_ENABLE_CPU_TIMEOUT 代表 CPU 使能超时时长,当 ENABLE_A
13、LL_CPUS 是TRUE 时,该值表示所有 CPU 的使能时长,当 ENABLE_ALL_CPUS 是 FALSE 时,在kernelCpuEnable()被调用时,它用来表示 CPU 的启动时长。VX_SMP_CPU_EXPLICIT_RESERVE 表示将指定 CPU 排除在“可使用 CPU-Affinity属性的 CPU 池 ”之外。它是一个字符串,若填写“2 3 7”,则代表 CPU2,3,7 不能使用CPU-Affinity 属性。即不能通过 taskCpuAffinitySet()分配任务到这些 CPU 上运行。当某个 CPU 被 VX_SMP_CPU_EXPLICIT_RESE
14、RVE 包含,唯一能够使他们恢复预留属性的方法是调用 vxCpuReserve()。4. 在多核 AMP 系统上配置 VxWorks SMP略5. 启动 VxWorks SMP在 WorkBench 开启后会有一个默认的 SMP 的 simulator,如图 4 所示:图 4 WR 自带的 SMP 虚拟机点击连接后启动,启动过程如图 5 所示,代表目前已经进入 VxWorks SMP 系统以及当前 CPU 的个数。图 5 SMP 虚拟机启动过程启动后在 SHELL 中输入 i 可以查看系统目前运行的任务,你会发现两个 idle 任务,它们分别运行在两个不同的 CPU 上。如图 6 所示。图 6
15、 SMP 系统任务运行情况6. VxWorks SMP 编程VxWorks 单核编程( UP)与 SMP 编程在多数情况下是一样的。类似的,多数 API 在UP 和 SMP 编程中是通用的。一些少数 UP 编程中的 API 不能在 SMP 中使用。与此同时,SMP 中的一些 API 在 UP 中使用时表现的不是 SMP 中的效果,而是默认 UP 的效果,或者压根就不能使用(例如,task spinlock 默认表现为 task lock) 。由于 SMP 系统的特殊性,因此 SMP 编程需要特别注意,尤其是在互斥/同步机制上,在使用的时候需要充分考虑如何提高系统的性能。在 VxWorks SM
16、P 系统中针对每个 CPU都有一个 idle 任务,这在 UP 中是没有的。Idle 任务是最低优先级(用户级任务是不能达到这么低优先级的) 。当 CPU 进出 idle 状态时,idle 任务会提供任务上下文,这可以用来监视 CPU 的利用率情况。当 CPU 无事可做时, Idle 任务的存在不会影响 CPU 进入睡眠状态(当电源管理开启时) 。可以使用 kernelIsCpuIdle()或者 kernelIsSystemIdle()这两个 API 查看一个指定 CPU 是否执行了 idle 任务或者所有 CPU 是否执行了 idle 任务。【注意】不要对 idle 任务进行挂起、关闭、跟踪
17、、改变优先级等一系列操作。SMP的互斥/同步机制SMP 编程与 UP 编程最大的一个不同就是互斥/同步 API 的使用。有一些 API 在这两种编程中都可以使用,而有一些则不同。此外,UP 编程中的一些隐式同步技巧(例如使用任务优先级替代显示同步锁等)在 SMP 中是不能用的。与 UP 系统不同,SMP 系统允许真正意义上的同时执行。即多个任务或多个中断可以同时执行。在绝大多数情况下,UP 系统中与 SMP 系统中的互斥/同步机制(例如,信号量、消息队列等)是一样的。但是,UP 中的一些机制(例如,关中断、挂起任务抢占机制以此来保护临界资源等)在 SMP 中是不适用的。这是因为这些机制阻碍了同
18、时执行的理念,降低了 CPU 的利用率,是的 SMP 系统向 UP 系统的回溯。SMP 编程与 UP 编程的一点不同是关于 taskLock()和 intLock()的使用上。SMP 提供了以下互斥/同步锁机制进行替代:a. 任务级、中断级的 spinlock;b. 任务级、中断级的 CPU-specific;c. 原子操作;d. 内存障碍(memory barrier)7. spinlock 互斥 /同步机制在 UP(单核)编程中通过信号量的方法可以实现 task 的互斥与同步,在 SMP 系统中可以继续沿用信号量的机制,而 spinlock 则用于替换 UP 编程中使用 taskLock(
19、)和 intLock()的地方。简介 taskLock()和 intLock()通过 taskLock()可以关闭系统的任务调度机制,调用 taskLock()的任务将是唯一获得CPU 运行资源的任务,直到这个任务调用 taskUnlock()为止。 intLock()与 taskLock()类似,intLock()用于关闭中断,使得中断 IRS 无法执行,直到调用者调用了 intUnlock()。Spinlock具有“满内存障碍”属性VxWorks spinlock 的获取与释放操作具备“满内存障碍”属性。 “满内存障碍”属性可以使读、写内存操作按照严格的顺序执行而不受到多 CPU 的影响。
20、因此,在申请与释放spinlock 之间进行更新的数据可以保证“更新顺序” 。Spinlock的种类Spinlock 分为两种:中断级 spinlock 和任务级 spinlock:a. 中断级 spinlock:可用于关闭本地 CPU 的中断。当任务调用中断级 spinlock 时,将会关闭本 CPU 的任务抢占机制;b. 任务级 spinlock:用于关闭本地 CPU 的任务抢占机制。(本第 CPU 指的是调用这些 API 的 CPU)Spinlock的作用以及使用说明与信号量不同的是,当一个任务试图申请一个已被另一个任务占用的 spinlock 时,该任务并不会进入阻塞状态(pend)
21、,而是可以继续运行,它会进入一个简单的、紧凑的循环直到 spinlock 得到释放。这种等待 spinlock 释放的状态可以用spinning和busy waiting来描述。在此,我们可以看出 spinlock 的优点和缺点。优点是:由于任务(或 ISR)在等待 spinlock 的时候没有进入 pend 状态而是继续执行(一个简单的循环用于获取 spinlock) ,这就避免了任务调用度以及上下文切换的消耗。缺点是:循环操作没有实际意义,会占用 CPU 资源。因此,只有在必要时才使用 spinlock。即占用 spinlock 的时间越短,spinlock 的优势发挥的越明显(例如 UP
22、 中的 taskLock()和 intLock()) 。否则,如果占用 spinlock 较长的时间,在 UP 编程中的缺陷(增加了任务和中断的响应时间)同样也会在多核编程中出现。在一个 CPU 上获取 spinlock,并不会影响另一个 CPU 上任务和中断的调度机制。当一个任务在持有 spinlock 的时候,该任务不能被删除。(1) 中断级 spinlock任务和中断都可以获使用中断级 spinlock。有两种中断级 spinlock:确定性的和非确定性的。【注意】在 UP 系统中,中断级 spinlock 与 intLock()和 intUnlock()的效果是一样的。确定性中断级 s
23、pinlock确定性中断级 spinlock 的最大特点是:公平、确定性。Spinlock 会分给第一个申请的中断或任务。申请的 spinlock 会屏蔽掉本地 CPU 的其他中断。如果是一个任务申请了中断用 spinlock,本地 CPU 的任务调度机制将被停止直到该任务释放 spinlock。Spinlock 确保了任务可以独占 CPU 完成一些操作。其他 CPU 上的中断和任务不会受到干扰。确定性中断级 spinlock 的 API 全部包含在 spinLockLib 中,API 如表 1 所示。表 1 确定性中断级 spinlock 的 APIAPI 描述void spinLockIs
24、rInit(spinlockIsr_t *pLock, /* pointer to ISR-初始化确定性中断级 spinlockcallable spinlock */int flags /* spinlock attributes */)void spinLockIsrTake(spinlockIsr_t *pLock /* pointer to ISR-callable spinlock */)获取确定性中断级 spinlockvoid spinLockIsrGive(spinlockIsr_t *pLock /* pointer to ISR-callable spinlock */)释
25、放确定性中断级 spinlock非确定性中断级 spinlock非确定性中断级 spinlock 提供了更高的性能,但是当多个 CPU 试图同时申请一个spinlock 时,它并不保证公平性和确定性。即非确定性中断级 spinlock 并不一定会把spinlock 分配给第一个申请者。它的优势在于中断响应时间较短,即当 CPU 等待获取spinlock 的时候,中断不会被屏蔽。API 如表 2 所示。表 2 非确定性中断级 spinlock 的 APIAPI 描述void spinLockIsrNdInit(spinlockIsrNd_t * spin /* pointer to spinlo
26、ck */)初始化非确定性中断级 spinlockint spinLockIsrNdTake (spinlockIsrNd_t * spin /* pointer to spinlock */)获取非确定性中断级 spinlockvoid spinLockIsrNdGive (spinlockIsrNd_t * spin,int key /* return value of spinLockIsrNdTake */)释放非确定性中断级 spinlock(2) 任务级 spinlock任务级 spinlock(中断不可调用该 spinlock)可以关掉本地 CPU 的任务切换机制,使持有 spinlock 的任务独占 CPU 完成一些操作。同时,它不会对其他 CPU 上的任务调度机制产生影响。【注意】SMP 中任务级 spinlock 等同于 UP 编程中的 taskLock()和 taskUnlock()API 如表 3 所示。表 3 任务级 spinlock 的 APIAPI 描述