在 Rust 中以原子方式处理内存区域

问题描述 投票:0回答:1

假设我们可以访问对齐为 8 且区域大小为 8 的倍数的连续内存区域。

现在,假设我想使用该区域的特定段,但我想要对其进行独占访问,因此我使用该区域的前 8 个字节来设置“忙”标志以及我要使用的段的长度。正在使用。

问题是,如果多个线程定期从同一区域分配一些内存,我需要以原子方式设置并随后清除该标志(在使用完它之后)以避免竞争条件。

使用一点

unsafe
,我们可以将最初的8个字节转换为
AtomicUsize
(假设是64位处理器),然后对其使用原子加载/存储/CAS操作,这应该可以解决并行访问的问题,至少在理论。

现在的问题是,我会违反一些编译器不变量并用这种方法引入 UB,还是完全合理的做法?

假设:段“分配”总是发生在正确(8字节)对齐的地址处,最初的8字节永远不会被意外覆盖或以非原子方式处理。

rust memory-management locking atomic undefined-behavior
1个回答
0
投票

这触及了内存模型的一些微妙部分。

首先出现的问题是,是否会同时对内存区域进行原子和非原子读/写。 混合原子和非原子读取(在 C++ 中是 UB)在 Rust 中被定义为不是 UB。然而,当其中任何一个是写入(原子或非原子)时,这就变成了 UB。所以你必须确保所有访问都是原子的。

然后就是你到底是如何获得指针的问题。如果您有对该区域的可变引用(

&mut [u8]
或类似的东西),您可以将其转换为
&mut Atomic
甚至有安全的API,尽管不稳定)。但是,不允许使用其他引用来访问该区域(因为引用可变)。

如果您有

共享参考 (&[u8]

),故事就会变得更加复杂。在 Stacked Borrows 下,将共享引用转换为对 
UnsafeCell
(原子包含)的引用是直接的未定义行为。另一方面,树借用允许这样做,但它不允许写入此
UnsafeCell
,只能读取。这甚至是内存模型中极少数在某种程度上是最终的部分,因为 LLVM 的 
noalias
 强制要求这样做,至少对于作为函数参数的共享引用而言(因此,可能会触发实际的错误编译)与此)。

如果你有一个从来都不是引用的原始指针(例如,该区域的所有用法都使用这个指针,那么以前它是否是可变引用并不重要),那么可以将其转换为

&Atomic

不是&mut Atomic
!),只要确保没有为非原子类型创建引用即可。

© www.soinside.com 2019 - 2024. All rights reserved.