首页 时政热点 科技头条 智能AI 安全攻防 数码硬件 开发者生态 汽车 游戏 社会热点 开源推荐 医疗健康 归档 标签 关于

使用 Iceoryx2 的 ByteAtomic 实现安全无锁原语

摘要

Safe Lock-free Primitives with iceoryx2's ByteAtomic Marika Lehmann - 28/07/2026 In multithreaded programming, a common scenario involves multiple threads reading from and modifying shared data concur...

the data and sequence counter atomic lock that shared this
2026-08-04 1 阅读 约6分钟阅读 elfenpiff
分享:
字号:
使用iceoryx2 的ByteAtomic 实现安全无锁原语 Marika Lehmann - 28/07/2026 在多线程编程中,常见的场景涉及多个线程同时读取和修改共享数据。如果此读写操作不是原子操作,则会发生数据争用。在 Rust 和 C++ 等具有几乎相同内存模型的语言中,这会导致未定义的行为。为了防止这种情况,可以使用锁来保护数据在读取时不被修改。然而,传统的锁定机制存在死锁的风险,这是不可接受的,特别是在安全关键和高可靠性系统中。在不使用阻塞锁的情况下缓解所描述的数据争用的常见方法是利用序列锁。序列锁包含共享数据和一个原子计数器,每当更新数据时,该计数器的值为奇数: rust struct SequenceLock < T : Copy + Send > { counter : AtomicUsize , data : UnsafeCell < T >, // 为写入者提供必要的内部可变性。使用序列锁,写入线程将序列计数器递增到奇数值,更新数据,然后将计数器递增到偶数值。读取器线程在复制共享数据之前和之后读取序列计数器。如果计数器发生变化或者当前为奇数,则表明数据被并发修改。然后,读取器丢弃损坏的副本并重试。问题:即使读取器检测到数据被修改并在使用前丢弃副本,复制非原子数据本身的行为仍然会触发未定义的行为。虽然顺序锁可以检测到数据争用的发生,但它并不能阻止它。因此,如果不将数据分解为更小的、单独的原子部分,目前不可能在 Rust 或 C++ 中实现正确的序列锁。这是一个已知问题,虽然不断有人提议将“原子 memcpy”1 2 引入 Rust 和 C++ 标准库,但我们还不能依赖该功能。针对安全关键和高可靠性系统,iceoryx2 提供了一个基于类似于序列锁的机制的无锁构造库。为了使这些构造安全且正确,我们需要一种方法来执行字节级别的原子内存复制,以确保不会发生数据竞争。这就是我们实现按字节原子包装器 ByteAtomic 的原因,我们将在以下部分中对其进行描述。虽然其概念很简单,但实现真正的安全性需要克服未初始化内存的微妙但关键的问题。为了防止上述数据竞争以及由此产生的未定义行为,iceoryx2 中的 ByteAtomic 在其内部类型上提供了按字节原子读写操作。该包装器仅保证每个字节都以原子方式更新/读取;它不提供更高级别的线程安全保证。用户仍然必须强制执行适当的同步(例如序列锁)以防止读取或写入损坏。包装器仅确保内存复制不是未定义的行为,但它本身并不保证数据完整性。当我们解决内存安全的复杂性时,包装器的实现进行了一些改进。我们的 ByteAtomic 包装器的初始版本如下所示: rust /// 编译时固定大小、共享内存兼容的 ByteAtomic。 #[repr( C )] pub struct FixSizeByteAtomic < T : Copy , const SIZE : usize > { data : [ AtomicU8 ; SIZE ], _inner_type : PhantomData < T >, } impl < T : Copy , const SIZE : usize > FixedSizeByteAtomic < T , SIZE > { pub fn new (value : T ) -> Self { // 创建一个包含传递值的新 ByteAtomic } pub fn read ( & self ) -> MaybeUninit < T > { // 将存储的值按字节原子方式复制到a MaybeUninit } pub fn write ( & self , value : T ) { // 按字节原子方式存储传递的值 } } 它被命名为 FixSizeByteAtomic,因为必须在编译时提供数组大小,因为 Rust 还不允许直接在结构定义中使用 core::mem::size_of::() 。一旦这成为可能,我们计划删除 SIZE 通用参数,删除运行时固定大小版本 RelocatableByteAtomic ,并将结构重命名为 ByteAtomic 。为了理解为什么实现必须发展,让我们看一下 new() 的最初的、简单的实现: rust pub fn new (value : T ) -> Self { let bytes : [ u8 ; SIZE]=不安全{transmute_copy(&value)};自{数据:字节。 map ( AtomicU8 :: new), _inner_type : PhantomData , } } 此版本的 new() 接受可复制值,执行 transmute_copy 到字节数组中,并将每个字节作为 AtomicU8 存储到 ByteAtomic 的数据字段中。这工作正常 - 除非 T 包含未初始化的内存,例如 MaybeUninit 或填充字节: rust #[repr( C )] struct Foo { bar : u8 , // 7 padding bytes baz : u64 , } transmute_copy 假设正在复制的值是目标类型的有效表示,在我们的
这篇文章对您有帮助吗?

订阅66必读

每日精选科技资讯,直达你的邮箱