【问题标题】:Interlocked read on readonly memory page triggers access violation只读内存页面上的互锁读取触发访问冲突
【发布时间】:2014-01-13 17:55:37
【问题描述】:

为了执行联锁读取,我使用了InterlockedCompareExchange 函数

value = InterlockedCompareExchange(ptr, 0, 0);

现在我偶然发现了不寻常的情况。
我想在只读(写保护)内存页上使用这个互锁的“读取”。结果是“访问冲突写入位置x”...

所以我猜这是设计使然?我了解InterlockedCompareExchange 通常用作“写入”指令。但是为什么在这种特殊情况下会包含访问冲突触发器呢?

为了执行互锁读取,InterlockedCompareExchange 的替代方案是什么?


更新:我尝试使用

value = InterlockedCompareExchange(ptr, 0, -1);

当我知道 ptr 处的值永远不会为负时,但这也会触发访问冲突,即使在这种情况下肯定没有任何写入发生。所以我想这是本指令的设计...
但是为什么以及如何解决这个问题?


更新 2: 详细了解我开始使用互锁读取的原因。
我故意把我的问题含糊其辞。我通常对这个主题感兴趣,即跳过缓存和原子读取,包括原子 64 位读取。

我开始疑惑的原因是 .NET
System.Threading.Interlocked.Read(ref Int64 location)
方法中使用CompareExchange 进行联锁读取。
MSDN 声明此 .NET Interlocked.Read 方法:“Read 方法在 64 位系统上是不必要的,因为 64 位读取操作已经是原子操作。在 32 位系统上,64 位读取除非使用 Read 执行,否则操作不是原子的。”

此外,MSDN 声明(C 语言)InterlockedCompareExchange64 函数在任何情况下都不允许未对齐的操作:“此函数的变量必须在 64 位边界上对齐;否则,此函数将在多处理器 x86 系统和任何非 x86 系统上运行不可预测”
因此,根据该文本,.NET 也不应该有未对齐的 Int64 变量。但是为什么要联锁阅读呢?

【问题讨论】:

    标签: readonly access-violation interlocked


    【解决方案1】:

    如果比较成功,内存必须是可写的。即使您知道由于您的程序逻辑而不会成功,这也是正确的。在 x86 上,这是由处理器强制执行的。根据处理器文档,cmpxchg 指令generates a write cycle even if the comparison fails

    目前尚不清楚联锁操作的意图是什么。如果您只想以原子方式读取值(,不撕裂),那么您可以简单地发出读取,因为正确对齐的读取和写入已经是原子的。如果您想以特定语义访问内存,您可以根据需要在指令之前/之后粘贴_ReadBarrier_WriteBarrierMemoryBarrier 或栅栏。 Starting in Visual Studio 2005,易失性读取使用获取语义执行,易失性写入使用释放语义执行。

    【讨论】:

    • 感谢您的回答!我在问题的 Update2 中留下了关于问题背后原因的解释。此评论框太小,无法容纳它。我想确认:“正确对齐的读取和写入已经是原子的”是否也适用于读取 64 位变量的 32 位代码?很抱歉,我从一开始就没有提到 64 位。我试图故意含糊不清,但我应该更明确。
    • @RolandPihlakas MSDN explains:“在 32 位 Windows 上,不能保证对 64 位值的读写是原子操作。”您可以查看您的 CPU 文档以查看例如 movq 是否在 32 位系统上是原子的,但这不是 Win32 涵盖的内容。另见this SO answer
    • 谢谢,我想这解决了这个问题。我开始使用 _mm_loadl_epi64 内在函数,它应该对应于 movq 指令,当从对齐的地址读取时,该指令在英特尔处理器中是原子的:intel.com/Assets/PDF/manual/253668.pdf 我假设这适用于所有 x86 处理器。对我来说 movq 的唯一问题是它需要 CPU 的 SSE2 支持。
    • 来自上述文档的引用:Pentium 处理器(以及之后的更新处理器)保证以下额外的内存操作将始终以原子方式执行: * 读取或写入在 64 位边界上对齐的四字... P6 系列处理器(以及之后的更新处理器)保证以下附加内存操作将始终以原子方式执行: * 对适合高速缓存行的高速缓存内存进行未对齐的 16 位、32 位和 64 位访问。
    • 想要指出的是,使用 Visual Studio 的 volatile 并依赖其发布/获取会导致严重的可移植性问题。当构建在其他编译器上时,代码将看似工作,但由于微妙的竞争条件而神秘地失败。
    猜你喜欢
    • 2015-07-31
    • 2017-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-17
    • 2019-05-25
    • 1970-01-01
    相关资源
    最近更新 更多