http://infocenter.arm.com/help/topic/com.arm.doc.dht0008a/DHT0008A_arm_synchronization_primitives.pdf
当访问共享资源必须限制同一时间只能有一个Agent时,软件必须进行同步。
共享资源: 共享内存、外设
Agent: 处理器、进程、线程
通过对表示共享资源的状态变量进行原子修改来实现管理。 这种原子修改要么成功,要么失败,同时对其他Agent可见。
简单的系统,可以通过在临界区代码上禁用中断; 在现代的多任务多核心系统上,这不是一个有效和安全的方法。现代的计算机架构提供硬件同步原语以安全的方式对内存位置进行原子更新。
软件同步接口
OS和平台库在硬件无关的背后对应用开发者隐藏了低层次的硬件原语。这些功能构成了应用编程接口(API)的一部分。软件同步原语有两种:
互斥体:一个标未锁定、解锁两种状态的变量; 尝试对已经锁定的互斥体进行加锁会导致执行被阻塞,直到持有互斥体的Agent对它进行解锁。 互斥体有时被称为锁、二值信号量。
信号量:一个可以原子增加和减小的计数器;尝试对一个小于1的信号量做减小操作将阻塞执行,直到另一个Agent对它做增加操作。
除了阻塞操作,API可以定义非阻塞的接口,当执行请求的操作失败时,不阻塞而是立即返回一个错误条件。
多任务系统中的同步
在多任务操作系统中,任何同步操作必须被保证行为正确,即使被上下文切换(Context switch)所中断也要如此。在不必跟其他处理器进行同步操作时,软件可以在更新同步变量时禁用中断来实现目的。在OS内核中,这可能是一个实现同步的有效方法,但对于应用层软件而言,系统调用带来的性能开销使它成为不切实际的方案。
多处理器系统中的同步
多核和多处理器系统引入了一个新问题,它们需要在整个系统上,原子式的对互斥体进行加锁、对信号量进行修改。 这需要系统来维护跟踪活动的同步操作的全局状态。
ARM架构的历史同步原语
SWP和SWPB指令在寄存器和内存间原子性的交换一个32位字或一个字节。从ARMv6架构开始,ARM废弃了SWP和SWPB。这意味着未来的架构不保证支持这些指令。ARM强烈建议使用新同步原语。
ARMv6架构新增
ARMv6架构引入了对内存位置的独占访问(exclusive access)概念,对于更新内存提供了更灵活的原子操作。同时引入了内存类型、内存访问排序规则,和用于显式有序访问内存的屏障指令。
ARMv6架构以Load-Exclusive和Store-Exclusive同步原语的形式引入了加载关联(Load Link)和条件存储(Store Conditional)指令, LDREX 和 STREX。从ARMv6T2开始,这些指令在ARM和Thumb指令集中都可以使用。Load-Exclusive和Store-Exclusive提供灵活、可伸缩的同步操作,取代了废弃的SWP和SWPB指令。
LDREX和STREX
LDREX和STREX指令把原子更新内存的操作划分为两个分开的步骤。它们与追踪独占内存访问的独占监控器(exclusive monitors)一起,提供了原子更新操作。Load-Exclusive和Store-Exclusive只能访问标记为Normal的内存区域。
LDREX 从内存加载一个字(word),并初始化用于跟踪同步操作的独占监控器。例如, LDREX R1, [R0] 实现Load-Exclusive,从地址R0取值放到R1中,并更新独占监控器(可能不止一个)。
STREX 条件式存储一个字到内存。如果独占监控器允许存储,更新内存并向目标寄存器放置0指示操作成功。如果独占监控器不允许,不更新内存并向目标寄存器放置1表示操作失败。这样根据内存操作的成功或失败来实现条件执行路径。 例如,STREX R2,R1, [R0] 执行Store-Exclusive操作:将R1的值条件存储到R0地址,R2指示成功或失败。
可选的独占访问大小
ARMv6K架构引入了LDREX和STREX的字节、半字和双字变体指令。
LDREXB, STREXB
LDREXH, STREXH
LDREXD, STREXD
ARMv7架构将这些加入到了A,R Profile的Thumb指令集中。 ARMv7-M支持字和半字,不支持双字变体指令。ARMv6-M不支持独占访问。
架构要求每一个Load-Exclusive指令必须只能跟对应的Store-Exclusive指令匹配使用。例如 LDREXB必须只能跟STREXB匹配使用。
独占监控器Exclusive Monitors
独占监控器是一个简单状态机,有open和exclusive两种状态。为支持处理器之间的同步,系统必须实现本地和全局两套监控器。一个Load-Exclusive操作更新相关监控器到exclusive状态,一个Store-Exclusive操作访问这些监控器以判断是否可以成功执行。 Store-Exclusive只在所有可访问的独占监控器处于exclusive状态才能成功。
本地监控器 Local monitors
每个支持独占访问的处理器有一个本地监控器。对于标记为非共享的内存位置的独占访问,都会跟本地监控器进行核对。对于标记为共享的内存位置的独占访问,会同时跟本地监控器和全局监控器进行核对。
例如,在Cortex-A8处理器上执行的软件, 必须在本地执行的应用之间实施同步,可以通过在非共享内存上使用一个互斥体来达到目的。Load-Exclusive和Store-Exclusive指令执行时只访问本地监控器。
本地监控器可以通过标记一个独占使用的地址来实现,或者包含一个跟踪Load-Exclusive和Store-Exclusive指令的状态机。这意味着一个对共享内存位置的Store-Exclusive操作,即使前面的Load-Exclusive来自完全不同的地址,也可能成功执行。 所以,可移植的代码不应该做出独占访问时会校验地址这样的臆断。
如果内存位置是可缓存的(cacheable), 则可能不经过外部总线而完成同步,并且不会被外部观察者所见,如系统中的其他处理器。
全局监控器 Global monitor
一个全局监控器跟踪对标记为共享内存区域的独占访问。任何以共享内存为目标的Store-Exclusive操作,必须同时核查它的本地监控器和全局监控器,然后再决定是否更新内存。
例如,在一个处理器上执行的软件,如果必须跟另一个处理器上执行的软件同步它的操作,可以通过位于共享内存的互斥体来实现。 Load-Exclusive和Store-Exclusive指令将访问本地和全局监控器。
对于全局监控器,或者说全局监控器的一部分,可能会联合本地监控器而实现,例如在一个实现了缓冲一致管理的系统上。
全局监控器可以为系统上的每个支持独占访问的处理器标记一个地址。当一个处理器从一个共享位置完成了Load-Exclusive操作,全局监控器为这个处理器标记了一个独占使用的地址。以下事件会重置全局监控器中的处理器N条目为open状态。
其他事件可能清空全局独占监控器,但它们取决于具体实现,并且可移植代码不应该依赖这些特性。
如果一个配置为共享的区域没有和一个全局监控器关联, 对此区域的Store-Exclusive操作总是失败,并在目标寄存器中返回0。
独占保留粒度 Exclusive Reservation Granule
当一个独占监控器标记了一个地址,用于独占访问的标记最小区域,称为独占保留粒度(ERG),它由实现定义,8-2048字节的范围,2的倍数。可移植代码不能假定它的大小。
重置监控器
当OS执行一个上下文件切换,它必须将本地监控器重置为open状态,以阻止错误发生。ARMv6K引入了Clear-Exclusive指令,CLREX,重置本地监控器。
在ARMv6基本体系结构和ARMv6T2中,必须通过对专用地址执行虚拟Store-Exclusive重置本地监视器。
数据中止异常后的监控器状态是架构未定义的,因此,ARM建议异常处理代码执行一个CLREX或虚拟Store-Exclusive指令。
如果上下文切换在一个执行Load-Exclusive之后,执行Store-Exclusive之前,调度出去这个进程,当进程恢复时,Store-Exclusive返回一个错误的否定结果,且不更新内存。 这不影响程序功能,因为进程可以立即得试此操作。
对于以上原因,ARM建议:
Load-Exclusive 和Store-Exclusive不要超过128字节。
Load-Exclusive 和Store-Exclusive之间不要执行显式的缓冲维护操作或数据访问。
为保证一个致的内存视角,架构上定义了软件必须实现数据内存屏障(DMB)操作:
在获取某个资源(如对互斥体加锁或减少信号量)和对资源做出访问之间;
在资源可用之前,如解锁互斥体或增加信号量。
数据内存屏障在ARMv7以前以cp15操作存在,ARMv7以专用指令形式存在。
在多核心系统上使用
用Load-Exclusive和Store-Exclusive做为模型的同步操作, 对于单核和多核系统是一样的,但是对于多核系统,你必须注意一些系统级别的含义。
带一致性管理的系统
ARM MPCore多核处理器包含一个Snoop Control Unit(SCU),用于维护处理器共享的跨内存区域里的1级数据缓存的一致性。 在这个组件中,每个核的本地监控器跟SCU一起操作,为那些标记为一致性的内存区域里的同步操作提供结合的本地和全局监控器。
当多个处理器尝试同时访问ERG块中的同步变量时,这可能会偶尔导致错误,或者在缓存之间传输数据引起的延迟。出于性能原因,显式地将经常访问的同步变量(至少ERG大小)在内存里分开放置是有效的。
不带一致性管理的系统
在处理器之间或多核之间用作同步操作的内存区域必须标记为共享。当一致性管理不可用或被禁用时,意味着这些区域不能缓存,同时全局监控器必须被实现以允许同步。