一次Rust重写基础软件的实践(一)

💡 原文中文,约3900字,阅读约需10分钟。
📝

内容提要

本文介绍了作者将一款C语言开发的基础软件转化为Rust语言的过程中遇到的问题和解决方案。作者在转化过程中遇到了与CAS操作实现相关的问题,提出了两种解决方案:使用asm!宏处理不同芯片平台的汇编代码,或者使用Rust代码对特定操作进行实现。作者选择了第二种方案,并给出了具体的代码实现。最后,作者指出两种解决方案各有利弊,需要根据实际应用场景进行选择。

🔎

延伸解读

跨平台兼容:C与Rust的宏差异

文章指出,C语言通过宏定义和嵌入汇编实现跨平台CAS,但Rust的宏与C宏差别不小,直接移植困难。作者面临的核心挑战是如何在Rust中处理芯片平台和操作系统级别的代码兼容。这提醒开发者,从C迁移到Rust时,不能简单照搬宏和汇编,需重新设计平台抽象层。

两种方案权衡:asm!宏 vs Rust实现

作者提出两种方案:使用asm!宏包裹不同平台汇编,或直接用Rust代码实现CAS逻辑。前者简单直接,但杂糅多平台汇编,维护复杂,尤其支持新平台如RISC-V时需额外控制逻辑;后者工作量大,但能消除平台差异。选择取决于项目对维护成本和性能的需求。

内存顺序选择:SeqCst的保守与正确

在Rust实现中,作者使用Ordering::SeqCst,并解释这是保证正确性而不太考虑效率优化的选项。SeqCst提供最强的顺序一致性,适合对正确性要求高的场景,但可能带来性能开销。开发者应根据实际同步需求选择更宽松的内存顺序,以平衡正确性与性能。

数据类型扩展的决策难题

作者在结论中提出,如果需要对多种数据类型(如uint32、int32、uint64、float等)进行比较交换,该如何选择方案?这没有标准答案,需结合具体场景。对于少量类型,Rust实现可能更简洁;对于大量类型,asm!宏或许更灵活。这反映了软件移植中权衡的普遍性。

❓

Q&A

为什么作者要将C语言开发的基础软件重写成Rust?

作者受到2022年谷歌使用Rust重写Android系统且所有Rust代码的内存安全漏洞为零的启发,因此尝试将一款C语言开发的基础软件转化为Rust语言。

在C语言中实现CAS操作时,不同平台有什么差异?

不同芯片平台的CAS操作汇编代码不同,例如x86-64使用lock cmpxchgq指令,ARM使用ldrex/strexeq指令,PowerPC使用lwarx/stwcx.指令。即使同一芯片平台(如Intel x86),在不同操作系统上的汇编实现也可能不同。

作者提出的两种解决CAS操作跨平台兼容问题的方案是什么?

第一种方案是使用asm!宏处理不同芯片平台的汇编代码;第二种方案是使用Rust代码对特定操作进行针对性的实现,以消除平台差异。

使用asm!宏方案有什么优缺点?

优点是简单直接,无需考虑具体汇编指令和代码;缺点是杂糅了不同平台的汇编代码,需要大量平台相关的逻辑控制,维护复杂,且支持新平台(如RISC-V)也需要额外工作。

作者最终选择了哪种方案,为什么?

作者选择了第二种方案(用Rust代码实现),因为软件中原子同步部分都是使用uint32进行的比较交换操作,而Rust刚好有对应的AtomicU32的CAS实现,因此第二种方案是最佳选择。

作者给出的Rust实现CAS操作的代码示例中,关键步骤是什么?

关键步骤是将自定义的my_atomic_uint32结构体指针转换为AtomicU32指针,然后调用AtomicU32的compare_and_swap方法,并指定Ordering::SeqCst内存顺序。

两种解决方案各有什么利弊?如何选择?

两种方案各有利弊:asm!宏方案简单但维护复杂;Rust代码实现方案工作量大但能消除平台差异。需要结合实际应用场景选择,例如如果需要支持多种数据类型(uint32、int32、uint64等)的CAS操作,则需权衡。

🏷️

标签

➡️

继续阅读