第一范文网 - 专业文章范例文档资料分享平台

VxWorks SMP多核编程指南 (3)

来源:用户分享 时间:2020-06-23 本文由沿途的风景 分享 下载这篇文档 手机版
说明:文章内容仅供预览,部分内容可能不全,需要完整文档或者需要复制内容,请下载word后使用。下载word有问题请添加微信号:xxxxxx或QQ:xxxxxx 处理(尽可能给您提供完整文档),感谢您的支持与谅解。

spinlockTask_t *pLock, /* pointer to task-only spinlock */ int flags /* spinlock attributes */ ) void spinLockTaskTake( 获取任务级spinlock spinlockTask_t *pLock /* pointer to task-only spinlock */ ) void spinLockTaskGive( 释放任务级spinlock spinlockTask_t *pLock /* pointer to task-only spinlock */ )

(3) Spinlock的使用注意事项

由于SMP系统允许任务的同时运行,因此在使用spinlock的时候需要注意以下事宜: a. spinlock最好用于短时间占用的情况;

b. 任务(或中断)一次只能申请一个spinlock。当一个已申请了spinlock的实体再一

次申请了另一个spinlock时,很有可能会造成死锁;

c. 任务(或中断)不能申请它已经持有的spinlock。这可能会造成死锁;

d. 持有spinlock的任务(或中断)不能再调用一些特殊函数(尤其是内核函数),由

于这些特殊函数本身持有spinlock,这种操作可能会导致死锁。

(4) Spinlock的调式版本

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

表 4 使用spinlock会出现错误的情况

使用的API spinLockTaskTake() 错误信息 一个中断任务使用了该API 申请了已持有的spinlock 嵌套申请spinlock spinLockTaskGive() spinLockIsrTake() spinLockIsrGive()

(5) Spinlock中限制使用的系统API

一个中断任务使用了该API 试图释放一个没有申请过的spinlock 申请了已持有的spinlock 嵌套申请spinlock 试图释放一个没有申请过的spinlock 当任务(或中断)持有spinlock时,一些系统API不能被调用(具体原因见Spinlock

的使用注意事项)。这样做为的是防止持有spinlock的任务或ISR进入内核临界区,这可能会导致死锁的发生。这种限制对于intCpuLock()也是适用的。这是因为有些内核API需要中断操作。

这些限制看起来好像使spinlock的运用受到影响,但是它们却是有必要的。Spinlock适用于进程间很快的同步/互斥情况。若将spinlock用在会进行大量操作——包括内核API调用等——的情况时,则会导致SMP性能的下降。这是因为当使用spinlock时,任务抢占机制以及中断都将会被关闭。图7列出了在使用spinlock和CPU lock时限制使用的系统API。

图 7 spinlock中限制使用的系统API

8. CPU-specific互斥机制

VxWorks SMP提供了一种基于CPU-specific的互斥机制,它可以严格限定互斥操作的范围在调用该操作的CPU(本地CPU)上执行。通过设计CPU-specific使得将UP代码转到SMP系统上变得容易。 (1) 中断级CPU-specific

中断级CPU-specific可以关闭本地CPU上的中断。例如,当任务A在CPU-0上运行一个本地CPU的中断锁操作,则该CPU将不再允许其他中断执行,直到任务A释放这个锁。SMP系统中其他的CPU将不会受到影响。

对于那些想要使用CPU-specific互斥机制的任务和ISR,必须使用CPU-Affinity将它们指定运行在本地CPU上,只有这样CPU-specific互斥才会有意义。

与spinlock一样,在执行中断锁的任务中有些系统API不能被使用(详见图7)。 中断级CPU-specific的API如表5所示。

【注意】在UP中,它们默认的操作与intLock()和intUnlock()一样。

表 5 中断级CPU-specific互斥API

API int intCpuLock (void); void intCpuUnlock( int lockKey /* lock-out returned by preceding intCpuLock() */ )

(2) 任务级CPU-specific

描述 当CPU-0上的任务或ISR调用了该函数后,则禁止在CPU-0上的一切中断调用。 恢复在CPU-0上的中断调用。 key 任务级CPU-specific可以关闭调用该API的CPU上的任务抢占机制。例如,当运行在CPU-0上的任务A调用了任务锁操作,则该CPU上将禁止任务切换,即该CPU上其他任务将不能得到运行,直到任务A释放了这个锁或执行了一个阻塞操作。

【注意】调用该操作的任务是不能被移交到另外的CPU上运行的,直到这个锁被释放。 SMP系统中其他的CPU将不会受到影响。对于那些想要使用CPU-specific互斥机制的任务和ISR,必须使用CPU-Affinity将它们指定运行在本地CPU上,只有这样CPU-specific互斥才会有意义。

任务级CPU-specific的API如表6所示。

【注意】在UP编程中,他们默认的操作与taskLock()和taskUnlock()类似。

表 6 任务级CPU-specific互斥API

API taskCpuLock() taskCpuUnlock()

描述 当CPU-0上的任务或ISR调用了该函数后,则禁止在CPU-0上的一切任务切换。 恢复在CPU-0上的任务切换。 9. Memory Barrier

在现代多核体系中,CPU需要对读、写操作完成重排序,为的是提高系统的整体性能。而在单核CPU中,这种重排序完全是透明的,因为无论系统如何对读、写操作进行排序,CPU都能确保任何读操作获取的数据都是之前已写入的数据。

在多核体系中,当一个CPU执行了一系列写内存操作时,这些写操作将会在CPU执行操作写到内存之前被排序。CPU可以将这些写内存的操作按任意顺序排列,无论是哪条指令先到达的CPU。类似的,CPU可以将多个读操作并行处理。

由于这种重排序的存在,两个有共享数据的任务不能保证:一个任务在CPU0上执行读、

写操作的顺序与另一个任务在CPU1获取对应数据的顺序是一致的。关于重排序问题有一个经典的例子:在一个双核CPU系统中,一个CPU正在准备工作,当设置一个bool变量为true时,告知另一个CPU这个工作准备就绪,在此之前,另一个CPU一直处于等待状态。这个程序的代码就像下面一样:

/* CPU 0 – announce the availability of work */ pWork = &work_item; /* store pointer to work item to be performed */ workAvailable = true;

/* CPU 1 – wait for work to be performed */ while (!workAvailable); doWork(pWork); /* error – pWork might not be visible to this CPU yet */

这个程序的结果很有可能是CPU1使用的pWork指针指向了不正确的数据,这是因为CPU0会重排序它的写内存操作,这就会导致CPU1在观察到workAvailable改变的时候而pWork还未被更新。

为了解决内存操作排序问题,VxWorks提供了一系列的”memory barrier”操作。这些操作的唯一目的就是提供一种方法可以确保CPU间操作顺序的一致性。memory barrier分为三个主要方面:读memory barrier,写memory barrier,满(读写)memory barrier。

【注意】VxWorks SMP提供了一系列同步原语来保护共享资源。这些原语包括:信号量、消息队列、spinlock等。这些原语中已经包括了满memory barrier功能,不用再添加其他的memory barrier操作来保护共享资源。

【注意】memory barrier不能用在用户模式的RTP app中。 (1) 读memory barrier

VX_MEM_BARRIER_R()宏定义提供读memory barrier。VX_MEM_BARRIER_R()会强制所有读操作进行排序。如果没有barrier,CPU会随意的为这些读操作进行排序。对于一个单核CPU而言不受影响。例如,CPU可以随意重排序一下读操作的顺序: a = *pAvalue; /* 读 可能发生在读pBvalue之后 */ b = *pBvalue; /* 读 可能发生在读pAvalue之前 */

通过在读操作间加入memory barrier,可以保证读的顺序,例如: a = *pAvalue; /* 读 发生在读pBvalue之前 */ VX_MEM_BARRIER_R(); b = *pBvalue; /* 读 发生在读pAvalue之后 */

在使用VX_MEM_BARRIER_R()后可以确保读数据的顺序是正确的。但是,这种保证只有在“写数据”能够保证顺序正确的前提下才能有效。即VX_MEM_BARRIER_R()和VX_MEM_BARRIER_W()宏定义必须一起使用。

(2) 写Memory Barrier

VX_MEM_BARRIER_W()宏定义提供写memory barrier。VX_MEM_BARRIER_W()会强制所有写操作进行排序。以下程序片段来自前面的代码,通过加入写memory barrier后对代码进行了改进:

pWork = &work_item;

VX_MEM_BARRIER_W(); workAvailable = true;

通过加入barrier可以确保pWork的更新一定先于workAvailable.

【注意】VX_MEM_BARRIER_W()并不是强迫将变量写入内存,而是指定了写的顺序 【注意】VX_MEM_BARRIER_W()必须与VX_MEM_BARRIER_R()一起使用。

(3) 读写(满)Memory Barrier

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

10. 原子的内存操作(原子操作)

原子操作是利用了CPU支持原子访问内存的特点。原子操作是一些不能被中断的操作的集合。原子操作为一组操作提供了互斥性(例如变量的自增、自减操作)。

使用原子操作更新一个数据,可以省去使用锁的步骤。例如,你想更新一个链表元素的next指针从NULL到非NULL,当你使用原子操作时,这个过程就不用使用中断锁了,这样可以使算法变得简单。

在调用者使用原子操作的时候,必须保证该操作所在的内存是可以访问的。若访问了一个不可访问的内存,会产生一个异常。

在vxAtmicLib库中提供了许多原子操作。如表7所示。需要注意的是vxAtmicLib还提供了这些原子操作的inline版本。例如,vxAtomicAdd_inline()。还提供了兼容SMP和AMP的版本。例如vxAtomic32Add()。原子操作可以在用户(RTP APP)、内核空间中使用。

表 7 原子操作API

API atomicVal_t vxAtomicAdd( 将两个值相加。 atomic_t * target, /* memory location to add to */ atomicVal_t value /* value to add */ ) atomicVal_t vxAtomicSub( 将两个值相减 atomic_t * target, /* memory location to subtract from */ atomicVal_t value /* value to sub */ ) atomicVal_t vxAtomicInc( atomic_t * target /* location to increment */ ) atomicVal_t vxAtomicDec( atomic_t * target /* location to decrement */ ) 将值增加1 memory 描述 将值减1 memory

搜索“diyifanwen.net”或“第一范文网”即可找到本站免费阅读全部范文。收藏本站方便下次阅读,第一范文网,提供最新高中教育VxWorks SMP多核编程指南 (3)全文阅读和word下载服务。

VxWorks SMP多核编程指南 (3).doc 将本文的Word文档下载到电脑,方便复制、编辑、收藏和打印
本文链接:https://www.diyifanwen.net/wenku/1098898.html(转载请注明文章来源)
热门推荐
Copyright © 2018-2022 第一范文网 版权所有 免责声明 | 联系我们
声明 :本网站尊重并保护知识产权,根据《信息网络传播权保护条例》,如果我们转载的作品侵犯了您的权利,请在一个月内通知我们,我们会及时删除。
客服QQ:xxxxxx 邮箱:xxxxxx@qq.com
渝ICP备2023013149号
Top