我在网上找到了几个地方,上面写着CLREX";必须";每当进入中断例程时都会被调用,我不明白。CLREX状态的文档(添加编号以便于参考):
(1)清除执行处理器的本地记录,即地址已请求独占访问。
(2) 使用
CLREX
指令将紧密耦合的独占访问监视器返回到其开放访问状态。这就消除了对内存进行虚拟存储的要求。(3) 实现定义
CLREX
是否也清除执行处理器的全局记录,即某个地址已请求独占访问。
我几乎什么都不懂。
我的印象是,按照文档中的示例写一些东西就足以保证原子性:
MOV r1, #0x1 ; load the ‘lock taken’ value
try: <---
LDREX r0, [LockAddr] ; load the lock value |
CMP r0, #0 ; is the lock free? |
STREXEQ r0, r1, [LockAddr] ; try and claim the lock |
CMPEQ r0, #0 ; did this succeed? |
BNE try ; no - try again ------------/
.... ; yes - we have the lock
为什么;本地记录";需要清理吗?我认为
LDREX
/STREX
足以保证从几个中断对地址的原子访问?也就是说,用于ARM的GCC使用LDREX
/STREX
编译所有C11原子函数,我看不到CLREX
在任何地方被调用。什么";对伪存储器的要求";第二段是指什么?
全局记录和本地纪录之间有什么区别?多核场景是否需要全球记录?
分别回答(并转述)您的三个问题:
1.为什么要清除访问记录
当强制执行严格的代码嵌套时,例如在处理中断时,通常不需要CLREX
。然而,在某些情况下,这很重要。想象一下,您正在为抢先操作系统内核编写一个上下文开关,它可以异步挂起一个正在运行的任务并恢复另一个任务。现在考虑以下病理情况,涉及使用LDREX
和STREX
操纵相同共享资源的两个同等优先级的任务(A和B):
Task A Task B
...
LDREX
-------------------- context switch
LDREX
STREX (succeeds)
...
LDREX
-------------------- context switch
STREX (succeeds, and should not)
...
因此,上下文开关必须发出CLREX
来避免这种情况。
2.避免了什么"虚拟存储要求">
如果没有CLREX
指令,则有必要使用STREX
来放弃独占访问标志,这涉及内存事务,因此如果您只想清除该标志,则速度会慢于需要的速度。
3.多核场景的"全球记录"是不是
是的,如果你使用的是单核机器,那么只有一条记录,因为只有一个CPU。
实际上,M7上的异常/中断不需要CLREX
,它似乎只是出于兼容性原因才包含在内。来自文档(c版):
CLREX能够与其他具有如果发生异常,则强制存储独占失败在加载独占指令和匹配的存储独占指令之间同步操作中的指令。在Cortex-M处理器中,本地独占访问监视器在异常边界,因此使用CLREX的异常处理程序是可选的。
因此,由于Cortex-M处理器在异常/中断进入/退出时清除了本地独占访问标志,这否定了CLREX
的大多数(全部?)用例。
关于你的第三个问题,正如其他人所提到的,你认为全球记录用于多核场景是正确的。根据实现定义的对本地/全局标志的影响,在多核处理器上可能仍然存在CLREX
的用例。
我可以理解为什么会有混淆,因为M7文档的初始版本不包括这些句子(更不用说ARM网站上的各种其他版本的通用文档了)。即使是现在,我甚至无法链接到最新的修订版。页面默认显示"版本a",您必须通过下拉框手动更改版本(希望将来会更改)。
更新
作为对评论的回应,提供了一个额外的文档链接。这是手册的一部分,描述了在特定说明文件之外使用这些说明(自第一次修订以来也一直存在):
如果:,处理器将删除其独占访问标记
它执行CLREX指令。
它执行STREX指令,而不管写入是否成功。
出现异常。这意味着处理器可以解决不同线程之间的信号量冲突。
在多处理器实现中:
执行CLREX指令仅删除处理器的本地独占访问标记。
执行STREX指令或异常会删除处理器的本地独占访问标记。
对可共享内存区域执行STREX指令还可以删除系统