vxWorks SMP 多核编程

 


多核编程指南 
       

  vxworks_kernel_programmers_guide_6.8 第24章 

 介绍 
是风河公司为VxWorks设计的symmetric multiprocessing(SMP)系统。
uniporcessor(UP)系统一样,具备实时操作系统的特性。 
VxWorks SMP系统的特点。介绍了VxWorks SMP的配置过程、它
UP编程的区别,还有就是如何将UP代码移植为SMP代码。 


 关于VxWorks SMP 
SMP是多核技巧中的一
OS运行在多个处理单元上,并且内存是共享的。另一种多核技巧
asymmetric multiprocessing(AMP)系统,即多个处理单元上运行多个OS。 

1) 技术特点 
CPU与处理器的概念在很多计算机相关书籍里有所介绍。但是,在此我们仍要对
SMP系统中的区别进行详细说明。 
:一个CPU通常使用CPU ID、物理CPU索引、逻辑CPU索引进行标示。一个
通常由系统固件和硬件决定。物理CPU索引从0开始,系统从CPU0开始启动,随
CPU个数的增加,物理CPU索引也会增加。逻辑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 
指的是一个处理器上有四个CPU。 
SMP系统上运行UP代码总会遇到问题,即使将UP代码进行了更新,也很难保证
SMP系统的特性。对于在SMP上运行的代码,我们分为两个级别: 
:虽然可以正常的运行在SMP系统上,但是并没有很充分的利用SMP系统
 
:不仅可以正常的运行在SMP系统上,而且还能很好的利用SMP系统
CPU使多个任务可以同时执行,提高系统的效率,比UP系统的效果更
 

2) VxWorks SMP OS特点 
单核编程(UP)与SMP编程在多数情况下是一样的。类似的,多数API在
和SMP编程中是通用的。一些少数UP编程中的API不能在SMP中使用。与此同时,
中的一些API在UP中使用时表现的不是SMP中的效果,而是默认UP的效果,或者
task spinlock 默认表现为task lock)。 
VxWorks的对称多处理器的一些特点: 
UP系统而言,处理多任务的方法是通过任务优先级对CPU资源
SMP系统则改变了这种方法,它是实实在在的任务、中断的同时执
CPU上执行,当然这需要OS的协调控
UP系统中多任务所谓的同时执行,其实只不过是CPU的快速切换,占有CPU的
SMP系统中,同时执行不是幻想而是实实在在存在的。 
VxWorks SMP系统中的任务调度机制与UP中的类似,都是基于优先
CPU上时,可以实现两个任务的同时执行。 
SMP系统允许任务同时运行的情况存在,因此,在UP系统中通过关中断、
SMP系统中将不再适用。这种在所有CPU上通过
SMP系统发挥它的特点,将SMP系统带回到
系统的模式。VxWorks SMP提供一套特殊的任务间、中断间同步/互斥的方法——即UP
taskLock()和intLock()等将会被VxWorks SMP提供的spinlock,原子操作以及
等机制替代。 
:默认情况下,任意任务可以运行在任意CPU上。VxWorks SMP提供了
CPU-Affinity的机制,即可以分配任务到指定CPU(CPU逻辑索引)上执行。 

3) VxWorks SMP 硬件特点 
SMP系统要求硬件必须具备对称多处理器。这些处理器必须是一样的,处理
可以平等的访问所有设备。VxWorks SMP必须遵循uniform memory access 
结构。 
1显示了一个双CPU的SMP系统 

 1 SMP硬件结构 
SMP系统中CPU的个数是多少,它们的重要特点是一样的: 
 内存对所有CPU可见,不存在“只属于某个CPU的内存”的情况。即任意CPU可以在
 
 每个CPU都有Memory Management Unit(MMU)。MMU可以使任务在不同的虚拟内存
RTP1的一个任务可以在CPU0上运行,与此同时,RTP2的一个任
CPU1上运行; 
 每个CPU可以访问所有设备。设备产生的中断可以通过可编程中断控制器发送到任意
上执行; 
 通过多CPU,任务和ISR可以实现同步;通过spinlock,任务和ISR可以实现互斥; 
 Snoop bus的作用是使CPU之间的data cache总是保持前后一致性。 

4) VxWorks SMP与AMP的对比 
SMP与AMP系统中对内存访问的对比如图2所示: 

 2 SMP系统对内存的占用情况 
SMP系统中,所有物理内存被所有CPU共享。内存空间可以用来保存VxWorks SMP
Real-Time Process(RTP)等。所有CPU可以读、写、运行所有内存。内核任务、用户
CPU中执行。 
SMP系统中,所有内存、设备被所有CPU共享,CPU之间的主要通讯是如何防止
 


 3 AMP系统对内存的占用情况 
AMP系统中,每个CPU对应一个VxWorks镜像的拷贝,它们只能被对应的CPU访
CPU1中执行的内核任务不可能在CPU0的内存中执行,反之亦然。对于RTP也
 
AMP系统中,一些内存是共享的,但是在这些共享内存中读写数据是严格受到控制
VxWorks镜像中传递数据等。硬件资源根据OS被划分,因此CPU之间
 


 VxWorks SMP配置说明 
的调式版本组件 
提供了spinlock的版本,这对调试SMP APP有帮助。 
INCLUDE_SPINLOCK_DEBUG的同时,最好要加入INCLUDE_EDR_ERRLOG组
spinlock的错误信息。 

配置参数组件 
组件中包含了一些对VxWorks SMP参数的配置,包括: 
代表VxWorks SMP的使能CPU个数。所有体系结构的最大使能
个数如下:ARM=4,IA32=8,MIPS=32,PowerPC=8,VxWorks Simulator=32。 
默认是TRUE,代表所有已配置的CPU使能。这个参数也可以设
FALSE,一般出于调试目的,此时只有逻辑CPU0是使能的,只有通过kernelCpuEnable()
CPU。 
代表CPU使能超时时长,当ENABLE_ALL_CPUS是
时,该值表示所有CPU的使能时长,当ENABLE_ALL_CPUS是FALSE时,在
被调用时,它用来表示CPU的启动时长。 
表示将指定CPU排除在“可使用CPU-Affinity属
CPU池”之外。它是一个字符串,若填写“2 3 7”,则代表CPU2,3,7不能使用CPU-Affinity
taskCpuAffinitySet()分配任务到这些CPU上运行。 
CPU被VX_SMP_CPU_EXPLICIT_RESERVE包含,唯一能够使他们恢复预留
vxCpuReserve()。 


 在多核AMP系统上配置VxWorks SMP 
 


 启动VxWorks SMP 
WorkBench开启后会有一个默认的SMP的simulator,如图4所示: 

 4 WR自带的SMP虚拟机 
5所示,代表目前已经进入VxWorks SMP系统以及当
CPU的个数。 

 5 SMP虚拟机启动过程 
SHELL中输入i可以查看系统目前运行的任务,你会发现两个idle任务,它
CPU上。如图6所示。 

 6 SMP系统任务运行情况 


 VxWorks SMP编程 
单核编程(UP)与SMP编程在多数情况下是一样的。类似的,多数API在
和SMP编程中是通用的。一些少数UP编程中的API不能在SMP中使用。与此同时,
中的一些API在UP中使用时表现的不是SMP中的效果,而是默认UP的效果,或者
task spinlock 默认表现为task lock)。 
SMP系统的特殊性,因此SMP编程需要特别注意,尤其是在互斥/同步机制上,
在VxWorks SMP系统中针对每个CPU都
idle任务,这在UP中是没有的。Idle任务是最低优先级(用户级任务是不能达到这
。当CPU进出idle状态时,idle任务会提供任务上下文,这可以用来监视
的利用率情况。 
CPU无事可做时,Idle任务的存在不会影响CPU进入睡眠状态(当电源管理开启时)。 
kernelIsCpuIdle()或者kernelIsSystemIdle()这两个API查看一个指定CPU是否执行
idle任务或者所有CPU是否执行了idle任务。 
不要对idle任务进行挂起、关闭、跟踪、改变优先级等一系列操作。 

的互斥/同步机制 
编程与UP编程最大的一个不同就是互斥/同步API的使用。有一些API在这两种
UP编程中的一些隐式同步技巧(例如使用任
SMP中是不能用的。 
UP系统不同,SMP系统允许真正意义上的同时执行。即多个任务或多个中断可以同
UP系统中与SMP系统中的互斥/同步机制(例如,信号量、
 
UP中的一些机制(例如,关中断、挂起任务抢占机制以此来保护临界资源等)
SMP中是不适用的。这是因为这些机制阻碍了同时执行的理念,降低了CPU的利用率,
SMP系统向UP系统的回溯。 
编程与UP编程的一点不同是关于taskLock()和intLock()的使用上。SMP提供了以
/同步锁机制进行替代: 
 任务级、中断级的spinlock; 
 任务级、中断级的CPU-specific; 
 原子操作; 
 内存障碍(memory barrier) 


 spinlock互斥/同步机制 
UP(单核)编程中通过信号量的方法可以实现task的互斥与同步,在SMP系统中
spinlock则用于替换UP编程中使用taskLock()和intLock()
 


和intLock() 
taskLock()可以关闭系统的任务调度机制,调用taskLock()的任务将是唯一获得CPU
直到这个任务调用
为止。intLock()与taskLock()类似,intLock()

无法执行,直到调用者调用了intUnlock()。 

具有“满内存障碍”属性 
spinlock的获取与释放操作具备“满内存障碍”属性。“满内存障碍”属性可
CPU的影响。因此,在申请与释放
之间进行更新的数据可以保证“更新顺序”。 

的种类 
分为两种:中断级spinlock和任务级spinlock: 
 中断级spinlock:可用于关闭本地CPU的中断。当任务调用中断级spinlock时,将
CPU的任务抢占机制; 
 任务级spinlock:用于关闭本地CPU的任务抢占机制。 
CPU指的是调用这些API的CPU) 

的作用以及使用说明 
当一个任务试图申请一个已被另一个任务占用的spinlock时,该任
pend),而是可以继续运行,它会进入一个简单的、紧凑的循环直
spinlock得到释放。 
spinlock释放的状态可以用’spinning’和’busy waiting’来描述。在此,我们可以
spinlock的优点和缺点。优点是:由于任务(或ISR)在等待spinlock的时候没有进入
状态而是继续执行(一个简单的循环用于获取spinlock),这就避免了任务调用度以及
CPU资源。 
spinlock。即占用spinlock的时间越短,spinlock的优势发
UP中的taskLock()和intLock())。否则,如果占用spinlock较长的时间,
UP编程中的缺陷(增加了任务和中断的响应时间)同样也会在多核编程中出现。 
CPU上获取spinlock,并不会影响另一个CPU上任务和中断的调度机制。当一
spinlock的时候,该任务不能被删除。 

1) 中断级spinlock 
spinlock。有两种中断级spinlock:确定性的和非确定
 
UP系统中,中断级spinlock与intLock()和intUnlock()的效果是一样的。 

spinlock 
spinlock的最大特点是:公平、确定性。Spinlock会分给第一个申请的中
spinlock会屏蔽掉本地CPU的其他中断。如果是一个任务申请了中断用
,本地CPU的任务调度机制将被停止直到该任务释放spinlock。Spinlock确保了任
CPU完成一些操作。其他CPU上的中断和任务不会受到干扰。确定性中断级
的API全部包含在spinLockLib中,API如表1所示。 
 1 确定性中断级spinlock的API 
描述 
 spinLockIsrInit( 
   spinlockIsr_t *pLock,  /* pointer to 
初始化确定性中断级spinlock 
   int flags     /* spinlock attributes */ 

 spinLockIsrTake( 
   spinlockIsr_t *pLock   /* pointer to 

获取确定性中断级spinlock 
 spinLockIsrGive( 
   spinlockIsr_t *pLock  /* pointer to 

释放确定性中断级spinlock 

spinlock 
spinlock提供了更高的性能,但是当多个CPU试图同时申请一个
时,它并不保证公平性和确定性。即非确定性中断级spinlock并不一定会把spinlock
CPU等待获取spinlock的时
API如表2所示。 
 2 非确定性中断级spinlock的API 
描述 
 spinLockIsrNdInit( 
   spinlockIsrNd_t * spin  /* pointer to 

初始化非确定性中断级spinlock 
 spinLockIsrNdTake ( 
   spinlockIsrNd_t * spin   /* pointer to 

获取非确定性中断级spinlock 

   spinlockIsrNd_t * spin, 
   int key /* return value of 
 */ 
释放非确定性中断级spinlock 

2) 任务级spinlock 
spinlock(中断不可调用该spinlock)可以关掉本地CPU的任务切换机制,使持
spinlock的任务独占CPU完成一些操作。同时,它不会对其他CPU上的任务调度机制产
 
SMP中任务级spinlock等同于UP编程中的taskLock()和taskUnlock() 
   API如表3所示。 
 3 任务级spinlock的API 
描述 
 spinLockTaskInit( 初始化任务级spinlock 
   spinlockTask_t *pLock,  /* pointer to 

   int flags      /* spinlock attributes 


 spinLockTaskTake( 
   spinlockTask_t *pLock   /* pointer to 

获取任务级spinlock 
 spinLockTaskGive( 
   spinlockTask_t *pLock  /* pointer to 

释放任务级spinlock 

3) Spinlock的使用注意事项 
SMP系统允许任务的同时运行,因此在使用spinlock的时候需要注意以下事宜: 
 spinlock最好用于短时间占用的情况; 
 任务(或中断)一次只能申请一个spinlock。当一个已申请了spinlock的实体再一
spinlock时,很有可能会造成死锁; 
 任务(或中断)不能申请它已经持有的spinlock。这可能会造成死锁; 
 持有spinlock的任务(或中断)不能再调用一些特殊函数(尤其是内核函数),由
spinlock,这种操作可能会导致死锁。 

4) Spinlock的调式版本 
的调试版本可以运行那些开发中使用了spinlock的程序对spinlock的情况进行
试。这需要添加INCLUDE_SPINLOCK_DEBUG组件。如果添加了
组件,则当由使用spinlock造成的系统异常进而重启后,相关信
4所示。 
 4 使用spinlock会出现错误的情况 

错误信息 
一个中断任务使用了该API 
spinlock 
spinlock 
一个中断任务使用了该API 
spinlock 

spinlock 
spinlock 
试图释放一个没有申请过的spinlock 

5) Spinlock中限制使用的系统API 
spinlock时,一些系统API不能被调用(具体原因见Spinlock
)。这样做为的是防止持有spinlock的任务或ISR进入内核临界区,这可能
这种限制对于intCpuLock()也是适用的。这是因为有些内核API需要中
 
spinlock的运用受到影响,但是它们却是有必要的。Spinlock适
/互斥情况。若将spinlock用在会进行大量操作——包括内核API调
SMP性能的下降。这是因为当使用spinlock时,任务抢占机
7列出了在使用spinlock和CPU lock时限制使用的系统API。 

 7 spinlock中限制使用的系统API 


 CPU-specific互斥机制 
SMP提供了一种基于CPU-specific的互斥机制,它可以严格限定互斥操作的
CPU(本地CPU)上执行。通过设计CPU-specific使得将UP代码转
SMP系统上变得容易。 
1) 中断级CPU-specific 
CPU-specific可以关闭本地CPU上的中断。例如,当任务A在CPU-0上运行一
CPU的中断锁操作,则该CPU将不再允许其他中断执行,直到任务A释放这个锁。
系统中其他的CPU将不会受到影响。 
CPU-specific互斥机制的任务和ISR,必须使用CPU-Affinity将它们
CPU上,只有这样CPU-specific互斥才会有意义。 
spinlock一样,在执行中断锁的任务中有些系统API不能被使用(详见图7)。 
CPU-specific的API如表5所示。 
UP中,它们默认的操作与intLock()和intUnlock()一样。 
 5 中断级CPU-specific互斥API 
描述 
当CPU-0上的任务或ISR调用了该函数后,
CPU-0上的一切中断调用。 
 intCpuUnlock( 
   int lockKey  /* lock-out key 

恢复在CPU-0上的中断调用。 

2) 任务级CPU-specific 
CPU-specific可以关闭调用该API的CPU上的任务抢占机制。例如,当运行在
上的任务A调用了任务锁操作,则该CPU上将禁止任务切换,即该CPU上其他任
A释放了这个锁或执行了一个阻塞操作。 
调用该操作的任务是不能被移交到另外的CPU上运行的,直到这个锁被释放。 
系统中其他的CPU将不会受到影响。对于那些想要使用CPU-specific互斥机制的
ISR,必须使用CPU-Affinity将它们指定运行在本地CPU上,只有这样CPU-specific
 
CPU-specific的API如表6所示。 
UP编程中,他们默认的操作与taskLock()和taskUnlock()类似。 
 6 任务级CPU-specific互斥API 
描述 
当CPU-0上的任务或ISR调用了该函数后,
CPU-0上的一切任务切换。 
恢复在CPU-0上的任务切换。 


 Memory Barrier 
CPU需要对读、写操作完成重排序,为的是提高系统的整体性能。
CPU中,这种重排序完全是透明的,因为无论系统如何对读、写操作进行排序,
都能确保任何读操作获取的数据都是之前已写入的数据。 
当一个CPU执行了一系列写内存操作时,这些写操作将会在CPU执行
CPU可以将这些写内存的操作按任意顺序排列,无论是哪条指
CPU。类似的,CPU可以将多个读操作并行处理。 
两个有共享数据的任务不能保证:一个任务在CPU0上执行读、
CPU1获取对应数据的顺序是一致的。关于重排序问题有一个
CPU系统中,一个CPU正在准备工作,当设置一个bool变量为
时,告知另一个CPU这个工作准备就绪,在此之前,另一个CPU一直处于等待状态。
 
– announce the availability of work */ 
 /* store pointer to work item to be performed */ 


– wait for work to be performed */ 

  /* error – pWork might not be visible to this CPU yet */ 
CPU1使用的pWork指针指向了不正确的数据,这是因为
会重排序它的写内存操作,这就会导致CPU1在观察到workAvailable改变的时候而
还未被更新。 
VxWorks提供了一系列的”memory barrier”操作。这些操
CPU间操作顺序的一致性。memory barrier分为三
memory barrier,写memory barrier,满(读写)memory barrier。 
VxWorks SMP提供了一系列同步原语来保护共享资源。这些原语包括:信号量、
spinlock等。这些原语中已经包括了满memory barrier功能,不用再添加其他的
操作来保护共享资源。 
memory barrier不能用在用户模式的RTP app中。 
1) 读memory barrier 
宏定义提供读memory barrier。VX_MEM_BARRIER_R()会强
barrier,CPU会随意的为这些读操作进行排序。对于一
CPU而言不受影响。例如,CPU可以随意重排序一下读操作的顺序: 
 /* 读 可能发生在读pBvalue之后 */ 
/* 读 可能发生在读pAvalue之前 */ 
memory barrier,可以保证读的顺序,例如: 
 /* 读 发生在读pBvalue之前 */ 

/* 读 发生在读pAvalue之后 */ 
VX_MEM_BARRIER_R()后可以确保读数据的顺序是正确的。但是,这种保证
VX_MEM_BARRIER_R()和
宏定义必须一起使用。 

2) 写Memory Barrier 
宏定义提供写memory barrier。VX_MEM_BARRIER_W()会强
memory barrier后对代
 

barrier可以确保pWork的更新一定先于workAvailable. 
VX_MEM_BARRIER_W()并不是强迫将变量写入内存,而是指定了写的顺序 
VX_MEM_BARRIER_W()必须与VX_MEM_BARRIER_R()一起使用。 

3) 读写(满)Memory Barrier 
宏定义提供读/写(满)memory barrier。
包括了VX_MEM_BARRIER_R()和VX_MEM_BARRIER_W()的
能。使用VX_MEM_BARRIER_RW()的代价要高于VX_MEM_BARRIER_R()或
的使用代价。Wind River不推荐使用VX_MEM_BARRIER_RW()。 


 原子的内存操作(原子操作) 
CPU支持原子访问内存的特点。原子操作是一些不能被中断的操作
。 
可以省去使用锁的步骤。例如,你想更新一个链表元素的
指针从NULL到非NULL,当你使用原子操作时,这个过程就不用使用中断锁了,这
 
必须保证该操作所在的内存是可以访问的。若访问了一
 
vxAtmicLib库中提供了许多原子操作。如表7所示。需要注意的是vxAtmicLib还提
inline版本。例如,vxAtomicAdd_inline()。还提供了兼容SMP和AMP
vxAtomic32Add()。原子操作可以在用户(RTP APP)、内核空间中使用。 
 7 原子操作API 
描述 
 vxAtomicAdd( 
   atomic_t * target, /* memory 

   atomicVal_t value /* value to add */ 
将两个值相加。 
 vxAtomicSub( 
   atomic_t * target, /* memory 

   atomicVal_t value /* value to sub */ 
将两个值相减 
 vxAtomicInc( 
   atomic_t * target /* memory 

将值增加1 
 vxAtomicDec( 
   atomic_t * target /* memory 

将值减1 
 vxAtomicOr( 
   atomic_t * target, /* memory 

   atomicVal_t value /* OR with this 

将两个值进行位或操作 
 vxAtomicXor ( 
   atomic_t * target, /* memory 

   atomicVal_t value /* XOR with this 

将两个值进行位异或操作 
 vxAtomicAnd ( 
   atomic_t * target, /* memory 

   atomicVal_t value /* AND with this 

将两个值进行位与操作, 

   atomic_t * target, /* memory 

   atomicVal_t value /* NAND with 

将两个值进行位非与操作 

   atomic_t * target, /* memory 

   atomicVal_t value /* set with this 

将一个值设定为另一个值 

   atomic_t * target /* memory 

将一个值清空 

   atomic_t * target,  /* memory 

   atomicVal_t oldValue, /* compare to this 

   atomicVal_t newValue /* swap with this 

对比或交换内存中的值。 


 CPU Affinity 
SMP提供了CPU Affinity这种机制。通过这种机制可以将中断或者任务分配
CPU执行。 
1) 任务级 CPU Affinity 
具有将任务分配给指定CPU执行的能力。从另一个角度来说,即将指定
预留给指定任务。 
的默认操作——任何任务可以运行在任何CPU上——这会根据系统的整体性能而
但是有些时候将指定任务分配给指定的CPU对系统性能是有帮助的。例如一个CPU上
CPU的cache中就只保存了这个任务所需
CPU之间切换的消耗。 
spinlock时,如果这些任务运行在不同的CPU上,
spinlock上。若将争夺同一个spinlock的任务指定在同一个
上运行,则这会给另一块CPU上执行其他程序带来便利。 
 CPU affinity的使用方法如下: 
 一个任务可以通过调用taskCpuAffinitySet()设置自己的CPU affinity,也可以设置
CPU affinity; 
 子任务会继承父任务的CPU affinity。一个任务中调用如下API就会自动继承CPU 
。 
 CPU affinity的API如表8所示。 
 8 任务级CPU Affinity的API 
描述 
TUS  taskCpuAffinitySet( 
   int      tid,       /* task ID */ 
   cpuset_t newAffinity /* new affinity set */ 
分配一个任务在指定CPU上执行。 
TUS  taskCpuAffinityGet( 
   int        tid,       /* task ID */ 
   cpuset_t*  pAffinity  /* address to store 

获得指定任务在哪个CPU上执行。 
和taskCpuAffinityGet()都使用cpuset_t结构对CPU信息进行标示。
CPU;后者获取指定任务的cpu_set_t。 
宏定义用于将cpuset_t清0(类似FD_ZERO),它必须被最先调用。 
宏定义在CPUSET_ZERO()之后使用。 

任务与CPU Affinity 
RTP任务会继承父任务的CPU Affinity属性。如果父任务没有CPU Affinity
RTP任务也没有CPU Affinity属性。如果父任务有CPU Affinity属性,则RTP任
CPU Affinity属性并仅运行在对应的CPU上。在使用rtpSpawn()时,
选项表示创建RTP时不继承CPU Affinity属性,即使父任务具
CPU Affinity属性。 

 CPU Affinity 示例: 
CPU1执行的全过程:【蓝色部分表示调
API】 
TUS affinitySetExample(void) 


Create the task but only activate it after setting its affinity */ 
taskCreate(“myCpu1Task”, 100, 0, 5000, printf, (int)”myCpu1Task executed on CPU 
”, 0, 0, 0, 0, 0, 0, 0, 0, 0); 


Clear the affinity CPU set and set index for CPU 1 */ 
(affinity); 
(affinity, 1); 

taskCpuAffinitySet(tid, affinity) = = ERROR) 

taskDelete(tid); 
return ERROR; 


Now let the task run on CPU 1 */ 
(tid); 

CPU affinity: 


(affinity); 
passing a tid equal to zero causes an affinity to be set for the calling task */ 
(0, affinity); 


2) 中断级CPU Affinity 
硬件需要可编程中断控制设备。VxWorks SMP利用这些硬件可以分配中断到指定
。默认情况下,中断是在VxWorks的CPU 0中触发的。 
CPU Affinity,可以将中断合理平均的分配到不同CPU上(而不是在一个
上存在很多中断)。 
CPU是在启动时发生的,当系统启动从BSP中读取中断配置
然后,中断控制器收到一条命令,该命令用于指示一条中断运行在指定的CPU
 


 将CPU预留给使用了CPU Affinity的任务(CPU预留机制) 
提供了一种机制可以将CPU预留给那些已经使用了CPU Affinity的任务。
CPU预留机制的任务抢占CPU资源,因此它提升了系
CPU预留机制的API如表9所示。 
 9 CPU预留机制API 
描述 
TUS  vxCpuReservedGet ( 
   cpuset_t *pCpuSet 
获取可预留CPU的集合 
TUS  vxCpuReserve( 
   cpuset_t cpus,   /* CPUs to be 

   cpuset_t *pReservedCpus /* CPUs 

预留CPU集合cpus,返回CPU预留的结果pReservedCpus  
TUS  vxCpuUnreserve( 
   cpuset_t cpus 
解除某个CPU的预留机制。 
CPU没有使用vxCpuReserve()时,所有CPU都是可以被预留的。可以
VX_SMP_CPU_EXPLICIT_RESERVE参数,将指定CPU排除在CPU预留池之外。
CPU池中的CPU才可以被预留。 
与vxCpuReserve()没有明确的调用顺序。前者是把任务分配给指定
CPU,这样可以防止该任务运行在其他CPU上。而后者限定了CPU上可以运行哪些任
 
CPU Affinity,则它的子任务将会继承CPU-Affinity属性;如
CPU预留给了使用CPU Affinity的任务,则这些任务的子任务将会在这个CPU上运
 

预留与任务级CPU Affinity的示例 
CPU以及设置一个任务级CPU affinity在预留CPU
【蓝色部分表示调用的关键API】 


cpuset_t cpuset;  /* Input argument to vxCpuReserve() */ 
cpuset_t resCpuSet;  /* Return argument from vxCpuReserve() */ 

/* Passing an empty cpuset as input reserves an arbitrary CPU */ 
CPUSET_ZERO(cpuset); 

vxCpuReserve(cpuset, &resCpuSet) = = OK) 

/* set affinity for current task */ 
if (taskCpuAffinitySet(0, resCpuSet) != OK) 
/* handle error */ 

/* handle error */ 


CPU并且为多个任务设置CPU affinity: 


extern int tids[3];   /* some task Ids */ 
int cpuIx[] = {1, 2, 4};  /* CPU indices to reserve */ 
cpuset_t cpuSet; 
cpuset_t tmpCpuSet; 
int i; 

/* Initialize cpuSet with the desired CPU indices */ 
CPUSET_ZERO(cpuSet); 
CPUSET_SET(cpuSet, cpuIx[0]); 
(cpuSet, cpuIx[1]); 
(cpuSet, cpuIx[2]); 

Reserve the specified CPUs */ 
vxCpuReserve(cpuSet, NULL) = = OK) 

for (i = 0; i < 3; ++i) 

 tmpCpuSet = CPUSET_FIRST_SET(cpuSet); 
 if (taskCpuAffinitySet(tids[i], tmpCpuSet) != OK) 
 /* handle error */ 

 CPUSET_SUB(cpuSet, tmpCpuSet); 

/* handle error */ 


 CPU信息及管理 
提供了一些API和宏定义用于获取及操作CPU的信息。 
1) CPU的信息及管理API 
和vxCpuLib库提供了用于获取CPU信息以及管理CPU的相关API。kernelLib
CPU API如表10所示。 
 10 CPU内核信息API 
描述 
 kernelIsCpuIdle( 
   unsigned int cpu /* CPU to query status 

查看指定CPU是否为空闲状态。返回TRUE表示CPU为空闲状态。 
查看所有可用CPU是否为空闲状态。返回
表示为空闲状态。 
TUS  kernelCpuEnable ( 
   unsigned int cpuToEnable  /* logical 

通过输入index参数使能CPU 
可以通过输入index参数来使能指定的CPU。一旦CPU使能,任务调
CPU上分配任务。所有CPU在默认情况下是使能状态,可以将组件
设置为FALSE,这样VxWorks SMP系统启动后只有CPU 0为使能状
kernelCpuEnable()可以使能指定的CPU。 
中的CPU API如表11所示。 
 11 CPU信息API 
描述 
返回在SMP系统中已配置的CPU个数 

CPU的个数 
返回当前CPU的索引(逻辑编号) 
返回当前CPU的ID(有体系结构变量定义
OS定义的逻辑编号) 
vxCpuConfiguredGet()返回的是配置在BSP中的VxWorks SMP系统的CPU个数。
CPU个数不一致。 
用vxCpuEnabledGet()返回的是系统中运行CPU的个数。这个值可能与
返回的值不一致,也可能与硬件中实际存在的CPU个数不一致 
的返回值类型为cpuset_t,因此我们需要注意:再给返回值赋值之前,
CPUSET_ZERO()将cpuset_t变量清0. 
返回的是当前调用任务使用的CPU索引(逻辑编号)。该编号在0和
之间(N是vxCpuConfiguredGet()的返回值)。需要注意的是:默认情况下,任务可以从
CPU跑到另一个CPU上执行,所以不能保证任务结束后所在的CPU索引与刚才使用
返回的值是一致的。除非该任务是分配运行在指定CPU上的,或者使用了
或者intCpuLock()。 

2) CPU相关变量以及宏定义 
提供了一组变量和宏定义,通过设置这些值可以对CPU的配置进行控制。 
cpuset_t,它用于标识配置在VxWorks SMP系统中的CPU。Cpuset_t的位值标识
CPU的逻辑索引,Cpuset_t的第一位标识了CPU0,第二位标识了CPU1,第三位标识了
,以此类推(它与CPU在硬件中的物理位置无关)。 
8个CPU的硬件系统,在BSP中为VxWorks SMP配置了4块CPU,通过
可以讲cpuset_t中的位值清0。调用vxCpuIndexGet(),它的返回值只会设
 
宏定义用于设置和清除CPU索引(通过改变cpuset_t的值)。这些宏定义如图12
 
 12 操控CPU信息的宏定义 
描述 
设置CPU的索引(只针对一个CPU进行设
 
设置CPU的索引(针对所有CPU进行设置) 
设置CPU的索引(除了调用该宏的CPU之外
CPU) 
清除一个指定的CPU索引(只针对一个CPU
 
清除所有CPU索引(针对所有CPU进行设
 
当指定索引存在于cpuset_t中时,返回TRUE, 
当cpuset_t中没有索引时,返回TRUE, 
原子地设置CPU的索引(只针对一个CPU
 
原子地清除CPU的索引(只针对一个CPU
 
cpuset_t进行操作,而是要通过上面的宏定义间接的对cpuset_t进行操
 


 查看任务性能API 
checkStack()可以查看所有任务栈的使用情况。在shell下输入checkStack就可以得
8所示。 

 8 通过checkStack检测任务栈情况 
表示任务栈的大小,CUR表示当前使用任务栈的大小,HIGH表示使用任务栈的
MARGIN表示从没有使用过的任务栈大小(其中MARGIN = SIZE - HIGH)。 
spy()可以上报任务在内核空间、中断、idle中的tick使用情况。在shell下输入spy
9所示。 

 9 通过spy查看CPU使用率情况 
开启了tSpyTask用于监控系统任务的使用情况。IDLE表示空闲任务的CPU占用情
 

 SMP性能优化 
的目的就是提高系统的性能。如果仅仅是简单的使用SMP的代码,并不能完全发
SMP的潜能。因此,在SMP代码的基础上还需要进行优化。 
算法是否能够提高系统性能很大程度上取决于算法并行性的程度以及多线程独立
CPU。一个很好的例子:图形压
 
SMP算法不好的话,那么同时执行两个线程的消耗将会抵消掉多个CPU所带来的
CPU需要争夺的数据,那么系统将会增大
 
SMP系统反而不如UP系统运行的快。最好的情
 

 
APP通过任务复制的方式变成多线程。一个典型的例子是:
ISR

OS的时候我们就
UP系统中,线程化只能增加任务的吞吐量,即虽然线程增
CPU本身,而线程化并不能提高性
UP系统上计算密集的APP,线程化不能帮上什么忙。但是在SMP系统
线程化可以有效地提高系统性能,这是因为SMP系统解决了CPU的瓶颈
 

Spinlock 
spinlock会潜移默化的影响中断和任务抢占机制。因此必须谨慎的使用spinlock,
 

 
关于使用协处理器的任务创建选项(VP_FP_TASK)必须被谨慎使用。
当一个任务在创建时开启了协处理器选项时,协处理器
同时,系统也会保存每一次的上下文切换。这对于那些虽然开启了协处理
 

vmBaseLib 
库是VxWorks MMU的管理库,它允许内核APP和驱动管理MMU。SMP OS
MMU的后备内存(TLB)的一致性。例如,CPU MPC8641D中有
TLB的一致性。其他CPU,例如MIPS体系结构家族,就没有这个能力。
OS进行对MMU一致性的保护了。 

CPU-Affinity 
APP和系统,分配指定任务或中断到指定CPU上执行可以提高系统的效率。 


 简单例子 
提供了一些测试程序,用来测试SMP的特点和性能。以下程序测试了I/O
 


 


VxWorks内核的方式(添加INCLUDE_SMP_DEMO组件),将这些测试
序链接到VxWorks SMP内核镜像中。测试源代码在
中。 
installDir/vxworks-6.x/target/src/demo/smp/smpLockDemo.c为例来说明关于SMP
 
 smpLockDemo.c用于测试VxWorks SMP的同步/互斥机制 
* 描述: 
*  这个demo描述了VxWorks SMP中的同步机制。在SMP系统中任务和ISR可以同时运
 行在不同的CPU上,这就涉及到同时访问共享数据的问题,对此,VxWorks SMP提供 
了一系列机制: 
* 信号量(Semaphore):可以使用信号量实现任务间的同步机制。例如,使用一个任务或
ISR“唤醒”另一个任务。 
* VxWorks事件(VxWorks Events):同信号量。 
* 原子操作(Atomic Operator):能够安全的读、写内存。例如,完成一些类型的全局的 
 自增操作。 
 Spinlock:它可以用在任务间、ISR间、任务与ISR间的同步。它一般用来对较多的共 
 享数据和临界资源进行保护。 

 本demo对以上同步机制进行了对比。通过SMP系统中多个任务对一个共享int型变量 
 操作的实例,来对比这些同步机制。 

 DEMO执行 
 本demo中包含了两个优先级一样的任务,每个任务都重复地(循环)增加一个共享计 
 数器。这个共享计数器是用户定义的一个累加值(即被更新次数)。每个任务还有一个 
 自己的计数器,在增加共享计数器的同时也增加自己的计数器。假设任务自己的计数器 
 不会出错,我们要看看使用不同同步机制的不同效果,即共享数据的值是否与两个任务 
 自身计数器的值之和一致。 
  
 以上过程重复5次,每次使用不同的同步机制: 
 1. 不使用同步机制:任务访问共享数据时不使用同步机制。 
 2. Spinlock:当任务累加共享数据的时候申请spinlock,然后再释放spinlock。 
 3. vxAtomicInc()原子操作:任务使用原子操作vxAtomicInc()对这个共享数据进行累加。 
 4. vxTas()原子操作:在任务中通过vxTas()设置/清除一个flag,将这个flag当做一个普 
 通信号量使用。当这个flag被清除时表示信号量不可用,当被设置时表示可用。任务 
 需要使用这个信号量对共享数据进行累加,然后再释放信号量。 
 5. vxAtomicAdd()原子操作:在任务中使用vxAtomicAdd()原子操作对共享数据进行累 
 加。 

 调用任务 
 在SHELL中输入 smpLockDemo 3 
 参数3表示任务更新共享数据和自己数据的时长(以秒为单位)。如果不填写参数,则 
 已2秒为默认值。 

 执行结果: 
 METHOD     TASK 0     TASK 1    SUM(COUNTS)  GLOBAL    RESULT 
 ---------        ---------     ---------     -----------        ---------      --------- 
 no-lock        0x253d8ae   0x253b31c  0x4a78bca      0x470327d   Failed 
 spinLocks      0x782f43    0x78265a   0xf0559d       0xf0559d    Passed 
 atomicInc      0x20b600d   0x20b4623  0x416a630      0x416a630  Passed 
 test-and-set     0x1509e72   0x150a628  0x2a1449a      0x2a1449a  Passed 
 atomic Add     0x1e25b2a   0x1e26d2a  0x3c4c854      0x3c4c854  Passed 

 METHOD栏表示任务为了更新共享数据而使用的同步机制。TASK0任务栏表示TASK0 
 自己的计数器值,TASK1任务栏表示TASK1自己的计数器值。计数器的值越大,说明 
 同步机制越好。SUM(COUNTS)栏表示前面两个栏之和(TASK0和TASK1栏的和)。 
GLOBAL栏表示共享数据的值。RESULT栏表示了SUM值是否与GLOBAL的值一致。 

 从上面的测试结果可以看出,不使用同步机制是不行的,即使对共享数据仅仅是一个小 
 小的自增操作,不使用同步机制也会造成错误。结果还可以看出,使用vxAtomicInc() 
 原子操作不仅安全,而且其效率比vxAtomicAdd()要高,而vxAtomicAdd()的效率比使 
 用Spinlock()要高。原子操作效率高于spinlock的原因在于它们就是为简单原子读写操 
 作设计的,而spinlock则是对相对复杂的同步机制设计的。test-and-set方法通过使用 
 vxTas()执行一个信号量,这种方法比原子操作慢,比spinlock快。但是,在数据需要极 
 端小心操作时,还是使用spinlock这种锁机制比较好。Spinlock是普通信号量更安全的 
 替代品。 
*  
* 下面是同样的程序、同样的周期在一个UP系统上运行的结果。通过结果我们看到,这 
里没有冲突、没有错误,这是因为没有真正意义的同时执行存在。但是,从计数的结果 
来看,性能还是要比前者好的。这是因为,由于任务不能同时执行,很少有对共享数据
的竞争存在。不过,这个例子从另一个方面说明:一个APP虽然有“同时执行” 
的能力,但是过多的“竞争”使得这种并行的好处大打折扣。 

* -> smpLockDemo 
METHOD    TASK 0    TASK 1    SUM(COUNTS)  GLOBAL    RESULT 
---------     ---------     ---------     -----------        --------       --------- 
no-lock      0x265d794  0x2675ee8  0x4cd367c      0x4cd367c    Passed 
spinLocks    0xaae678   0xaaefaf    0x155d627      0x155d627    Passed 
atomicInc    0x23c5135  0x23c7018  0x478c14d      0x478c14d    Passed 
test-and-set   0x1a84dd6  0x1a864c0  0x350b296      0x350b296    Passed 
atomic Add   0x20b9c80  0x20bb843  0x41754c3      0x41754c3    Passed 
* / 
在此只对关键部分进行说明,因此部分代码就不在写了,大家可以查看它的源文件
【蓝色的是本文介绍的关于SMP的一些API】 

 

 lockDemoWorkersReady = FALSE; 


 smpLockDemo  smpLockDemo入口函数(也是SHELL命令) 
*  -> smpLockDemo <number of secs>, <number of tasks>, <[TRUE, FALSE]; (affinity) 
*  这个函数会开启两个任务用于同时更新一个共享数据,与此同时,更新自己的一个数据。 
<secs>参数表示使用不同同步机制进行测试的时间(默认为2s),即每种算法会给<secs> 
进行测试。 
* / 
TUS smpLockDemo ( 
   unsigned int secs,   /* The minimum life time of a worker task */ 
   unsigned int reqNumOfTasks,  /* number of tasks */ 
   BOOL  setAff   /* do task have affinity */ 


unsigned int availCpus = vxCpuConfiguredGet (); //获取已配置CPU个数 
 numOfTasks; 
eventsToWait = 0; 
 //这是一个枚举,表示不同机制 

if (reqNumOfTasks = = 0)   numOfTasks = availCpus; 
                    numOfTasks = reqNumOfTasks; 

   secs = 5; 


return ERROR; 

AIT_ALL, WAIT_FOREVER, NULL) != OK) 

return ERROR; 


 

 locksDemoTasksInit初始化、激活、设置CPU-Affinity任务。默认情况下,任务的 
CPU-Affinity属性是关闭的。locksDemoTasksInit创建了数个任务。 


   int   secs,  /* how long to run  */ 
   unsigned int numOfTasks, /* number of tasks evolved */ 
   BOOL  affIsOn, 
   unsigned int * eventsToWait, /* Events to wait mask  */ 
   LOCKS_DEMO_TYPE lockMethod /* Method of global count lock */ 


cpuset_t     taskAffinity;     
   unsigned int    eventWorkerDone;  
     i = 0; 

  //workerTid是一个数组,用于记录任务ID 

 

/* Task's number is the bit to send an event on */ 
   eventWorkerDone = 1 << i; 

/* Add event to the receive list for the Main tasks receives */ 


/* Create the specified number of tasks */ 
   if ((workerTid[i] = taskCreate ("worker", WORKER_TASKS_PRIORITY, 0, 
    5000, (FUNCPTR)workerEntry, secs,  taskIdSelf(), numOfTasks, 
    eventWorkerDone, i, lockMethod,0, 0, 0, 0)) == ERROR) 

    return ERROR; 

/*  
 * Set affinity for each task if requested and that number of tasks 
 * the number of CPU's available.  
 */ 
 if (affIsOn == TRUE) 
 { 
    taskAffinity = 1 << i; 

 /* Set affinity the task affinity with the task index */ 
    if (taskCpuAffinitySet (workerTid[i], taskAffinity) != OK)  
 { 
  return (ERROR); 
 } 

 } /* setAff == TRUE */ 

 /* Activate task ... */ 
 if (taskActivate (workerTid[i]) != OK) 
 {  
    return (ERROR); 
 }  
 i++; /* Increment iteration */  

 workerEntry 开启任务的入口。在开启任务之前要确保lockDemoWorkersReady被设置
TRUE,这样可以允许所有任务同时开启。 


* / 

   int  secs,   /* the lenght of demo duration      */ 
   int  parentTask,  /* Id of creator task       */ 
   unsigned int numOfTasks, 
   UINT32 eventWorkerDone, /* unique Id to indicate this task is done  */ 
   unsigned int threadNum,  /* task sequence number       */ 
   LOCKS_DEMO_TYPE lockMethod  /* method used to lock the global count     */ 

 
int stopTick; 

/* Don't start until all workers are ready */ 

/* Update global and local count */ 
if (locksDemoGlobalCountOp (lockMethod, threadNum, numOfTasks) != OK) 

    printf ("spinLocks fairness test failed"); 
    eventSend (parentTask, eventWorkerDone); 
    break; 
}       
     
/* Keep incrementing while before we reach end time */ 
if (tickGet() > stopTick) 

    /* Inform that worker is done */ 
    eventSend (parentTask, eventWorkerDone); 
    break; 
} /* tickGet() > stopTick */ 

 locksDemoGlobalCountOp 对一个共享数据和任务的私有数据进行累加。 
* 在这个函数中,会根据不同的同步机制进行运算。它有三个参数:第一个参数表示使用 
的同步机制;第二个参数表示调用该函数的任务ID,这个参数用于累加任务私有的数 
据;第三个参数表示任务个数。本函数会对五种同步机制进行运算。 
* / 

   LOCKS_DEMO_TYPE lockMethod,   /* Means of mutual exclusion */ 
   unsigned int    threadNum, 
   unsigned int    numOfTasks     /* Number of tasks   */ 


/* Check the method we are computing operation for */ 
   switch (lockMethod) 

case methodTaskSpinLock:  

    /* use a fair spinlock  */                             
    SPIN_LOCK_TASK_TAKE (&fairTaskSyncLock);  

    /* make sure that we only take a fair turn */ 
    varsToUpdate [threadNum]++;   /* Update my local count */ 
    varsToUpdate [0]++;    /* increment shared copy */ 
    SPIN_LOCK_TASK_GIVE (&fairTaskSyncLock);    /* release the lock  */ 
     break; 

case methodIsrSpinLock:  
    /* use a spinlock  */                             
    SPIN_LOCK_ISR_TAKE (&isrSyncLock);  
 
    varsToUpdate [threadNum]++;      /* Update my local count */ 
    varsToUpdate [0]++;       /* increment shared copy */ 
    SPIN_LOCK_ISR_GIVE (&isrSyncLock);  /* release the lock      */ 
     break; 

case methodIsrNdLock:  
    /* use a spinlock  */                             
    key = spinLockIsrNdTake (&isrNdLock);  
 
    varsToUpdate [threadNum]++;      /* Update my local count */ 
    varsToUpdate [0]++;       /* increment shared copy */ 
    spinLockIsrNdGive (&isrNdLock, key); /* release the lock     */ 
     break; 

case methodAtomicInc: 
    varsToUpdate [threadNum]++;      /* Update my local count */ 

    /* Atomiccally increment global count */ 
    vxAtomicInc ((atomic_t *) varsToUpdate );  
    break; 

case methodVxTas: 
    /* use test-and-set */                             
    while ((vxTas ((void *)&tasVar) != TRUE));  
      
    varsToUpdate [threadNum]++;     /* Update my local count */ 
    varsToUpdate [0]++;      /* Update global count  */ 

    VX_MEM_BARRIER_RW(); /* prevent above accesses from leaking */ 
    vxAtomicSet ((atomic_t *)&tasVar, 0); 
    break; 

case methodVxAtomicAdd:  
    /* Update my local count */  
    varsToUpdate [threadNum]++;  
     
    /* increment shared copy */ 
    vxAtomicAdd ((atomic_t *) varsToUpdate , 1);  
    break; 

default: 
case methodLockNone: 
    /* update my local count */          
    varsToUpdate [threadNum]++;  
     
    /* Update the global count */ 
    varsToUpdate [0] = varsToUpdate [0] + 1 ;  
    break; 
} /* endSwitch */ 
   return OK; 

 向VxWorks SMP系统移植代码 
SMP系统移植代码最关键要考虑的就是对称多处理允许任务与任务间、任务与ISR
ISR与ISR间可以同时执行。 
VxWorks UP向SMP移植代码需要一些步骤。移植的过程需要用到不同的多任务机
,还有一些互斥/同步机制等等。在移植代码时,我们需要将taskLock()替换成
,类似的地方还有很多。 
OS设备需要考虑的一些问题,还有在移植过程中的
 
1) 代码移植步骤 
VxWorks UP向SMP移植APP的模式和推荐步骤。 
VxWorks UP代码移植到当前版本的VxWorks SMP时,使
 
 将原来的UP代码移植到当前VxWorks UP系统中 
VxWorks UP 6.previous (或更早)系统的代码移植到VxWorks UP 6.current系
API替换成当前版本中支持的API,要把一些与
不兼容或不支持的API替换掉。 
 将当前VxWorks UP代码移植为当前VxWorks SMP代码 
VxWorks UP 6.current系统的代码移植到VxWorks SMP 6.current。在这个过
bug(例如死锁等)。硬件的替换也包含在这
CPU从UP到SMP)。 
 为提升SMP性能优化代码 
 
代码移植过程如图10所示。 

 10 UP代码向SMP的移植过程 

2) UP与SMP编程中不兼容的API 
13列出了UP与SMP编程中不兼容的API和LIB库。在UP代码向SMP代码移植
API: 
 13 UP与SMP中不兼容的API 
UP的API SMP对应的API 备注 
 使用显示同步机制,例如信号
spinlock 见 “任务的隐式同步机制”小节 
cacheLib库函数 修改了对应函数的用法 见“cacheLib限制”小节 
vmBaseLib库函数 修改了对应函数的用法 见“vmBaseLib限制”小节 
替换为spinLockLib, 
intCpuLock(),
 见“同步与互斥机制”小节,见“任务锁”小节 

替换为信号量、原子操作 见“同步与互斥机制”小节,见“RTP中的任务锁”小节 
taskVarLib库函数 __thread存储类 见“任务变量管理”小节 
库函数 __thread存储类 见“任务本地存储”小节 
CPU-specific全局变量 替换为CPU-specific全局变
 见“SMP CPU-specific变量和UP全局变量”小节 
SMP内存不
  见“内存访问属性”小节 
VxBus的驱动 适用于VxBus的驱动 见“驱动与BSP”小节 
的BSP SMP的BSP 见“驱动与BSP”小节 
的boot loader SMP的boot loader  

3) RTP应用与SMP 
VxWorks UP系统中,RTP(用户模式)应用对于互斥/同步机制的限制要比内核代码
VxWorks SMP中,RTP(用户模式)可以使用信号量、原子操作,但
spinlock,内存障碍以及CPU-specific这些互斥机制。此外,使用semExchange()可以
give和exchange操作。 

4) 任务的隐式同步机制 
是一个支持多任务的OS,VxWorks和它的APP是支持可重入的。因此,当任

A释放了一个信号量给任务B得以使其运行,这是一个显示的同步机制。另一
VxWorks SMP中使用。例如,如果一
A生成了一个低优先级的任务B,希望在任务A释放CPU之前任务B不
SMP系统中是不成立的。 
需要仔细查阅所有使用了与生成任务
API的代码: 
 创建任务的API 
taskSpawn(),rtpSpawn()等一些创建新任务的API。在SMP系统中,无论新的任务
API返回后在另一块CPU上

 
 会激活一个处于waiting中的pend任务的API 
semGive(),msgQSend(),eventSend()等一些会激活处于waiting中的pend任务的
,即使被激活的任务优先级比调用以上API函数的任务低,被激活的任务也有可能
 
VxWorks UP系统中,一个任务可以通过使用intLock()在保护临界资源,
ISR访问临界资源。当ISR访问临界资源时,不会使用显示
UP系统中运行ISR时,任务是不能运行的(驱动会使用很多ISR,
intLock()时,这些ISR会排队等待工作)。而在SMP系统中,ISR工作的同时任
因此,在ISR中必须使用显示互斥机制。关于ISR的spinlock
spinlock互斥与同步”大节。 

5) 同步与互斥机制 
SMP系统中允许真正意义上的“同时执行”存在,因此VxWorks UP与VxWorks SMP
/互斥机制有所不同。 
VxWorks UP和VxWorks SMP系统中的用法是相同的。但是关于中断锁和任
 
VxWorks SMP中,一个任务和一个ISR可以同时运行,这在VxWorks UP中
ISR之间的互斥机制也就随着系统的变化而变化。任务与
之间一个通用的同步机制就是二进制信号量,这种机制在VxWorks SMP系统中同样奏
UP代码中使用二进制信号量的地方在VxWorks SMP系统中不用修改。同样的
VxWorks事件(event)机制。 
ISR唤醒了一个任务(可以通过释放一个二进制信号量或发一个事件,
,该任务必须立即运行在另一个CPU上。 

6) UP中API在SMP中的变异 
UP与VxWorks SMP提供的API大多数是相同的,也有一部分在函数行为上
SMP系统的需求,即它们的使用有一定的限制。 

限制 
中的cache API是围绕UP系统设计的。包括:使能、使无效cache,清除
中数据,cache天生具有CPU-specific属性,它们统统设定为本地CPU的cache。在
系统中,这种天生的CPU-specific属性变得没有意义。VxWorks SMP支持的系统提供
cache一致性,这种一致性包括SMP系统CPU之间的,也包括内存子系统与设备内存
VxWorks SMP中关于cache限制和行为的改
 
和cacheDisable()。能够使硬件cache保持一致性的唯一方法就是使cache
VxWorks SMP中每个CPU的cache都要处于使能状态,不可
VxWorks SMP中调用cacheEnable()总会返回OK,而调用
总是返回ERROR。ERRNO是S_cacheLib_FUNCTION_UNSUPPORTED. 
以及cacheInvalidate(). 为了确保硬件cache一致性,这些API
VxWorks SMP中调用这些API,它们不会进行任何操作除了返回
。 
,cacheUnlock()。VxWorks SMP中不支持这些API。如果调用,将会返回
错误码是S_cacheLib_FUNCTION_UNSUPPORTED 
。该函数在VxWorks SMP中的使用方法与VxWorks UP中相同。 

限制 
SMP不提供交换内存页属性的API。在一个SMP系统中,RAM空间在CPU
如果系统RAM中的一个单独页属性被修改了,以至于它不再是硬件一致性
OS对这个页的操作(例如spinlock、共享数据结构等等)将会产生一个高风险
这种不可知的行为甚至在页属性改变后许久还能发生。这种情况很难通过
查到,这是因为在SMP中我们都是基于硬件一致性进行工作的。 
与vmStateSet()。在UP中,这些API用于修改虚拟内存中单独页的属
SMP系统中一个页的cache属性不能被修改。调用这些函数都会返回ERROR,并且
S_vmLib_BAD_STATE_PARAM. 

7) 在SMP中不支持的UP API以及SMP提供的替代API 
中的一些API在VxWorks SMP中不能使用,这是因为VxWorks SMP提供
VxWorks SMP针对对称多处理特点提供了这些API的替代品。 

intLock()和intUnlock() 
VxWorks UP中,当一个任务(或ISR)调用了intLock(),则它阻止了VxWorks调用
。这个API典型的用法是保证任务间、任务与ISR间、ISR间对临
这种机制在SMP系统中不再适用,VxWorks SMP提供了以下替代的方
 
 如果中断锁用来在一块内存上进行虚拟(pseudo)原子操作,那么原子操作将是一个好
 
 如果中断锁用在任务间的互斥机制,那么信号量或者任务级spinlock将会是一个好的替
Spinlock申请/释放的操作要比信号量快,因此对于要求性能的、占用时间较短的
spinlock比较合适。而信号量用于保护占用时间较长的临界资源时比
 
 如果中断锁用在任务与ISR间,或者ISR间,则中断级 spinlock比较合适; 
 如果中断锁用在任务间的互斥机制,并且所有参与互斥的任务有相同的CPU affinity,
taskCpuLock(); 
 如果中断锁用在任务间、任务与ISR间,或者ISR间,并且参与互斥的所有任务和ISR
CPU affinity,那么就可以继续使用intCpuLock()。 

taskLock()与taskUnlock() 
VxWorks UP系统中,当一个任务调用了任务锁API,则它会禁止系统中其他任务的
API的典型用法是保证临街资源的互斥访问。 
VxWorks UP中,内核API taskLock()会把任务调度机制挂起。这种机制在SMP系统
VxWorks SMP提供了以下替代的方法: 
 信号量; 
 原子操作; 
 任务级spinlock。Spinlock申请/释放的操作要比信号量快,因此对于要求性能的、占用
spinlock比较合适; 
 如果中断锁用在任务间的互斥机制,并且所有参与互斥的任务有相同的CPU affinity,
taskCpuLock()。 

中的任务锁:taskRtpLock()与taskRtpUnlock() 
RTP APP中使用taskRtpLock(),使得调用该API的进程不能再调用其他任务。与
一样,taskRtpLock()在SMP中不再适用。 
SMP中调用taskRtpLock()会引发致命错误并值得进程终止。可以使用信号量或原子
 

taskVarLib 
提供了VxWorks UP中的任务变量设备,它不适用在SMP系统中。多个使用
taskVarAdd()和taskVarDelete()
VxWorks SMP中不再适用。可以使用__thread存储类进行替换。 

tlsLib 
为VxWorks UP 用户模式的RTP APP提供了任务本地存储API。但这些API在
中不再适用。这是因为,使用相同任务变量的多个任务可能在同时执行。这些tlsLib
API包括:tlsKeyCreate(), tlsValueGet(), tlsValueSet(), tlsValueOfTaskGet(), 
可以使用__thread存储类进行替换。 

8) SMP CPU-specific变量以及UP全局变量 
UP中全局对象中一些(例如errno)在SMP中是属于不同CPU实体的(CPU-specific 
)。而另外一些在SMP中是不可访问或不存在的。 

变量 
API间接访问的SMP CPU-specific变量包括:errno, taskIdCurrent, intCnt, 
 
从一个程序员的角度来看,errno就像一个包含了当前运行任务(或ISR)错误
VxWorks SMP可以透明的管理像errno这样的CPU-specific变量。【注意】
errno,也不要在没有包含errno.h头文件的C或C++代码中直接访问errno。 
。在SMP中没有这个全局变量。在SMP中必须使用taskIdSelf()替换UP
taskIdCurrent. 
。在SMP中,指定中断在指定CPU上运行。intCnt用来跟踪在指定CPU上运行
SMP中,要使用intCount()替换UP中的intCnt。 
。它用来标识ISR在指定CPU上运行。只有当INCLUDE_ISR_OBJECTS
这个全局变量才可用。在SMP中,必须使用isrIdSefl()替换UP中的isrIdCurrent。 

UP中出现的全局变量 
VxWorks UP中存在,或者在VxWorks SMP中不能被用户代码访问,
vxIntStackBase, vxIntStackEnd, kernelIsIdle, windPwrOffCpuState. 
该变量标志了中断栈的基地址。在VxWorks SMP中,每个CPU有一个
处理中断,这是因为在多个CPU上的中断可能会同时发生。没有API可以
 
该变量标识了每个CPU中断栈的终止地址。没有API可以访问这个全局
 
在VxWorks UP中,该变量用于指出当前系统是否为idle。SMP中没有这个
 
该变量标识了指定CPU的电源管理状态。没有API可以访问这个
 

9) 内存访问属性 
SMP系统中,为了支持内存一致性,必须确保每个CPU可以看到一样的内存上下文。
CPU体系结构,一些内存访问属性不能满足内存一致性的需求。 

10) 驱动与BSP 
SMP的驱动与BSP开发必须遵从本章叙述的开发规则。驱动还必须符合
驱动模式。BSP为了提供VxBus支持,必须提供与VxWorks UP不同的reboot处理
CPU列举、中断查找与分配等等。 

 

  • 5
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
VxWorks是一种实时操作系统,其架构设计具有以下特点。根据引用中提到的讲义,VxWorks 653系统架构可以供有兴趣的朋友学习参考。该架构使用了多核技术,并且每个核心都有自己的MMU(内存管理单元)。这使得每个核心可以使用不同的虚拟地址执行任务,例如在CPU0上执行一个任务,在CPU1上执行另一个任务。 在多核环境下,任务与中断服务可以跨CPU同步,而互斥量需要使用spinlock(自旋锁)进行同步。在SMP(对称多处理)架构上,中断锁不被用作互斥手段。 综上所述,VxWorks的架构设计充分考虑了实时性和多核处理的需求,通过分离的MMU和适当的同步机制,实现了任务的并发执行和核间的通信。这种架构使得VxWorks适用于各种实时应用场景。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [VxWorks 653 系统架构讲义](https://download.csdn.net/download/leejey/10827554)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [vxWorks SMP架构](https://blog.csdn.net/benjorsun/article/details/82757254)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值