我正在研究我的无锁数据结构库的下一个版本。
我在ARM上使用LL / SC。
要将LL / SC用作LL / SC(而不是模拟CAS),LDREX和STREX之间必须有一个STR。
现在,我已经编写了代码并且可行。
然而,我担心的可能是它不起作用。
如果你访问与LL / SC目标相同的缓存行,我已经在PowerPC上阅读了,你打破了LL / SC。
所以我在想,如果我的STR目标与我的LL / SC目标在同一个缓存行上,那么战争,我已经死了。
现在,LL / SC目标和STR目标始终位于不同的malloc()中,因此它们直接位于同一缓存行中的可能性很小(我可以通过填充LL / SC目标来保证这一点)它从缓存行边界开始并填充缓存行。)
但如果STR目标位于内存中的正确(错误!)位置,则可能存在错误共享。
查看LDREX / STREX文档,它描述了“物理地址”方面的独占访问。这意味着寄存器宽度粒度,而不是缓存行宽度粒度。
这就是我的问题 - LDREX / STREX对使用寄存器宽度粒度或缓存线宽粒度的内存访问的敏感度是多少?
答案 0 :(得分:1)
ARM使用Exclusive Monitors通过load-linked-store-conditional实现对内存的独占访问。 [1]具有重要性的所有细节,我要说的是以下内容:
独家预约颗粒
当专用监视器标记地址时,可以使用最小区域 被标记为独占访问被称为独占访问 颗粒(ERG)。 ERG是实现定义的,范围为8-2048 字节,以两个字节的倍数表示。便携式代码不能假设 关于ERG大小的任何事情。
所以,在我看来,你有点不幸。大多数真正的实现都可能保留一个小的值,但据我所知,基本的ARM架构并不能保证,但也许有经验丰富的人会发现我错了。 :) 仍然,有点LL / SC的所有实现都是某种形式的弱LL / SC,所以你几乎永远不能完全确定LL和SC之间的存储不会总是杀死SC,或者大多数时间,或者可能永远......这只是建筑和实现的依赖,我个人坚持使用LL / SC在紧密的循环中实现CAS并像往常一样使用它。
[1] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0008a/CJAGCFAF.html
答案 1 :(得分:1)
注意,LDREX / STREX并没有像许多人认为的那样做。它们适用于多处理器系统,单处理器系统应考虑使用swap。 ARM文档通常非常好,但在这种特殊情况下它们有很大的差距。 Linux一直在不正确地使用这些指令,并且已经被具有单处理器ARM内核的公司注意到(Linux有许多ARM相关的错误,因为人们在没有适当研究的情况下添加代码,每个出来的版本都必须修复)。如果您在单处理器系统上有L1缓存,那么你可以,因为缓存支持该访问类型,如果它遇到AXI总线,AMBA / AXI规范告诉硬件工程师,对于单处理器系统,你不需要支持该事务类型。不幸的是,ARM ARM / TRM告诉软件工程师你应该停止使用swap并开始使用LDREX / STREX,这是不一致的,一方告诉不要这样做,另一方告诉他们这样做并没有什么好处。
这不是您的问题的答案,只是关于那些试图教育人们正确使用和涉及风险的指示的一般信息。 (是的,在那里,完成了,因使用这些指令而被烧毁,必须修补linux(在其他Linux补丁之上))
编辑....更多细节
在ARM ARM中:
Historically, support for shared memory synchronization has been with the read-locked-write operations
that swap register contents with memory; the SWP and SWPB instructions described in...
...
ARMv6 provides a new mechanism to support more comprehensive non-blocking shared-memory synchronization primitives
that scale for multiple-processor system designs.
...
The swap and swap byte instructions are deprecated in ARMv6. It is recommended that all software
migrates to using the new synchronization primitives.
...
Uniprocessor systems are only required to support the non-shared memory model, allowing them to support
synchronization primitives with the minimum amount of hardware overhead.
...
Multi-processor systems are required to implement an address monitor for each processor.
STREX:
<Rd> Specifies the destination register for the returned status value. The value returned is:
0 if the operation updates memory
1 if the operation fails to update memory.
MemoryAccess(B-bit, E-bit)
if ConditionPassed(cond) then
processor_id = ExecutingProcessor()
physical_address = TLB(Rn)
if IsExclusiveLocal(physical_address, processor_id, 4) then
if Shared(Rn) == 1 then
if IsExclusiveGlobal(physical_address, processor_id, 4) then
Memory[Rn,4] = Rm
Rd = 0
ClearExclusiveByAddress(physical_address,processor_id,4)
else
Rd = 1
else
Memory[Rn,4] = Rm
Rd = 0
else
Rd = 1
ClearExclusiveLocal(processor_id)
AMBA / AXI规范
The ARLOCK[1:0] or AWLOCK[1:0] signal selects exclusive access, and the RRESP[1:0]
or BRESP[1:0] signal (see Table 7-1 on page 7-2) indicates the success or failure
of the exclusive access.
...
If the master attempts an exclusive read from a slave that does not support exclusive
accesses, the slave returns the OKAY response instead of the EXOKAY response. The
master can treat this as an error condition indicating that the exclusive access is not
supported. It is recommended that the master not perform the write portion of this
exclusive operation.
...
b00 OKAY
b01 EXOKAY
...
ARLOCK/AWLOCK
b00 normal access
b01 exclusive access
因此软件方面,ARM ARM告诉我们使用LDREX / STREX而不是交换,部分原因是它扩展到多处理器,共享内存,系统。 但他们还告诉我们,单处理器系统不需要支持共享内存同步。因此,即使从软件方面也有一个线索,你应该三思而后行......
我们从STREX的描述中知道,如果它返回了独占rd = 0那么它就可以工作了。如果rd = 1那么它不是排他性的(或其他原因)。 LDREX和STREX成对完成,共享内存系统逻辑在同一地址查找该对,硬件验证两者之间没有其他访问权限。你是谁担心介入两者之间? 1)你,如果你打断/交换,并且很幸运2)另一个使用该内存的处理器。从我记忆中来看,linux所做的是进入一个紧密的无限循环,
while(1)
{
ldrex
strex
if(rd==0) break;
}
现在在单处理器系统上,ARM ARM建议他们不需要支持共享访问,因为它更简单(为什么需要添加这种复杂性?)。
作为程序员,您不会看到什么。 ARLOCK或AWLOCK是为ldrex和strex设置的,如果你正在实现共享访问,那么你关心这些位。如果您正在实现共享访问,那么如果两者之间没有访问,则将EXOKAY返回到strex。 EXOKAY是一个b01,它在strex伪代码中是独占全局,并且rd = 0.如果硬件返回OKAY,b00,则它不是独占的,而对于strex,rd = 1。然后AMBA / AXI规范说,如果你不支持共享系统,可以返回OKAY进行独占访问。因此,在没有实现独占访问的单处理器上,strex可以和/或总是返回OKAY,永远不会EXOKAY。这意味着strex永远不会得到rd = 0并且linux会在无限循环中挂起。
这里真正的linux错误是我们当时看到的代码说(如果(ARMv6或更新版本)然后使用LDREX / STREX,否则使用SWP。修复bug(如果(ARMv6或更高版本和多处理器)然后使用LDREX / STREX,否则使用SWP。
这意味着其他任何想要使用LDREX / STREX的人都会因此而引起我的注意。
现在你问,缓存与它有什么关系? L1缓存位于处理器内核中,它不会出现在AXI / AMBA总线上。它为strex返回EXOKAY,和/或它完全实现共享。因此,如果L1缓存打开,那么您将获得一个EXOKAY(第一次或最终,我不确定)。
现在你问,如果有缓存未命中怎么办?首先,如果L1缓存关闭,则它会在没有可缓存位的情况下命中L2缓存边界。因此,L2缓存将按原样传递它,并且它作为独占传递出去。在L1缓存打开的情况下,命中它会返回EXOKAY,如上所述(最终或总是,不知道)如果L1是未命中,则L1执行缓存行填充,它执行可缓存的NOT LOCKED读取。这导致L2要么命中,要么错过,如果L2未命中,那么它会转到供应商特定逻辑,在这种情况下返回OKAY但是没关系,因为它不是LOCKED,无论如何它是正常访问。一旦l2和l1被填充,则L1执行原始传输并返回EXOKAY。
现在这里是踢球者,首先在硬件中实现这一点是浪费和风险,所以我希望单处理器ARMv6和更新版本不返回EXOKAY,你必须根据具体情况进行测试。第二,它是一个让缓存关闭的Linux运行的PITA。事实上,这需要一些工作。所以你不太可能在linux中看到这个。但问题出在那里,人们已经看到了它,而且每当你自己使用这些指令时,你应该小心使用它们。使用裸机编程来测试系统是否会挂起应该是非常简单的,应该花几秒钟/几分钟来编写代码。将系统置于可以尝试该代码的状态可能需要更长时间(中断引导加载程序,使用jtag跳转等)。