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

这篇具有很好参考价值的文章主要介绍了一次Rust重写基础软件的实践(一)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

受到2022年“谷歌使用Rust重写Android系统且所有Rust代码的内存安全漏洞为零” [1] 的启发,最近笔者怀着浓厚的兴趣也顺应Rust 的潮流,尝试着将一款C语言开发的基础软件转化为 Rust 语言。本文的主要目的是通过记录此次转化过程中遇到的比较常见且有意思的问题以及解决此问题的方法与大家一起做相关的技术交流和讨论。

问题描述

在项目转化过程中我遇到了一个与 CAS (Compare and Swap) [2] 操作实现相关的问题,在计算机科学中CAS 是多线程/协程中用于实现同步的原子指令。该软件针对不同的芯片平台,通过在C语言中根据芯片平台的类别进行宏定义并嵌入相应的汇编代码来实现CAS操作。我知道不同芯片平台对应的 CAS 操作的汇编代码是不一样的 [3],例如:

x86-64 (Intel/AMD) 需要类似如下汇编代码块:

lock cmpxchgq [destination], rdx

ARM 需要类似如下汇编代码块:

ldrex r1, [destination]
    cmp r1, r2
    strexeq r1, r2, [destination]

PowerPC 需要类似如下汇编代码块:

lwarx r0, 0, destination
    cmpw r0, r1
    bne retry ; branch if not equal
    stwcx. r2, 0, destination
    bne retry ; branch if store failed

然而如下面的代码片段所示,即使该软件使用相同的Intel x86芯片平台,但是在不同的操作系统平台上其实现的汇编指令也有可能是不一样的。

  • C头文件中 cas_operation.h 的部分代码如下:

#if defined(__i386) || defined(__x86_64__) || defined(__sparcv9) || defined(__sparcv8plus)
typedef unsigned int slock_t;
#else
typedef unsigned char slock_t;
#endif
extern slock_t my_atomic_cas(volatile slock_t *lock, slock_t with, slock_t cmp);
#define TAS(a) (my_atomic_cas((a), 1, 0) != 0)
#endif
  • 对应实现的x86汇编文件 cas_operation.s 的部分代码如下:

my_atomic_cas:
#if defined(__amd64)
 movl       %edx, %eax
 lock
 cmpxchgl   %esi, (%rdi)
#else
 movl    4(%esp), %edx
 movl    8(%esp), %ecx
 movl    12(%esp), %eax
 lock
 cmpxchgl %ecx, (%edx)
#endif
 Ret

众所周知虽然Rust也有宏定义的包 Macros,但是目前也与C语言的有不小的差别。因此,在做转化的过程中如何做到芯片平台和操作系统级别的代码兼容则是我遇到的最大挑战。

解决方案

想到两个解决方案:

  1. 使用asm! 宏去处理不同芯片平台的汇编代码

  2. 使用 Rust代码对特定的操作进行针对性的实现

第一种方案比较简单,只需要在代码中使用std::arch::asm 包,然后使用 asm! 宏(类似 println! 宏)去包裹不同平台的汇编代码即可,这也是最直接最容易想到的解决方案,而且无需考虑具体的汇编操作实现的指令和代码。但是这方法杂糅了很多的不同平台的汇编代码,同时需要Rust做很多额外的平台相关的逻辑控制,对这些控制逻辑部分代码的维护也是一个持久且复杂的工作。比如对新的平台指令 RSIC-V 的支持也要纳入其中。

第二种方案则需要考虑具体的操作逻辑,然后通过Rust代码去实现与汇编指令相同的逻辑,虽然有较大的工作量,但是这种方案可以消除由于芯片和系统平台不同带来的各种汇编代码实现的差异。 关于第一种方案的实现读者可以参照文档 Inline assembly [4] 中去做。针对 CAS 操作的第二种方案的实现则是本文主要提出的一种解决方案,而本文以类似Rust u32类型的 CAS 操作为例子实现其代码,在 my_compare_and_swap.rs 中会有如下代码段实现:

use std::sync::atomic::{AtomicU32, Ordering};

pub type uint32 = libc::c_uint;
pub struct my_atomic_uint32 {
 pub value: uint32,
}

impl my_atomic_uint32 {
    #[inline]
    pub fn compare_and_swap(&self, expected: uint32, newval: uint32) -> bool {
        let atomic_ptr = self as *const my_atomic_uint32 as *const AtomicU32;
        let atomic = unsafe { &*(atomic_ptr) };
        atomic.compare_and_swap(expected, newval, Ordering::SeqCst) == expected
    }
}

pub fn my_compare_and_swap_u32_impl(
    mut ptr: *mut pg_atomic_uint32,
    mut expected: *mut uint32,
    mut newval: uint32,
) -> bool {
 let atomic = &*ptr;
 atomic.compare_and_swap(*expected, newval)
}

下面我来解释一下上面的代码。由于是从 C 转到 Rust,因此我使用了 Rust 的 libc 包来自定义 uint32类型。然后通过自定义struct my_atomic_uint32 来对uint32进行CAS原子操作的包裹,同时对于此 struct实现其 inline 的compare_and_swap 操作函数。在该函数的实现中最关键的是将my_atomic_uint32的实体转化为一个AtomicU32的常量(注意需要在 Rust 代码文件开头使用 std::sync::atomic::{AtomicU32, Ordering} [5]),然后通过调用 AtomicU32 的compare_and_swap 来最终实现 uint32 的 CAS 操作。另外对于Ordering::SeqCst内存顺序 [6] 的选择也是比较考究的一个话题,这里我使用 SeqCst实际上是一个在保证正确的情况下不太考虑效率优化问题的选项。代码的最后my_compare_and_swap_u32_impl 则是对外使用的 u32 的 CAS 操作(事实上该软件主要也是需要实现 uint32 的 CAS 操作)。

结论

在本例中由于刚好有对应AtomicU32的CAS 实现,而且软件中整个原子同步的代码部分都是使用uint32进行的比较交换操作,因此我选择第二种方案则是最佳选择。由此可知上述的两种解决方案其实是各有利弊的,我必须结合实际的应用场景才能去做决定。那么这里有一个问题,如果需要对许多数据类型(比如uint32, int32, uint64, int64, float, float32, float64……)进行比较交换操作,又该做何种选择呢?这也许是仁者见仁智者见智的。

关于作者

张怀龙曾就职于阿尔卡特朗讯,百度,IBM等企业从事云计算研发相关的工作。目前就职于 Intel 中国,担任云原生开发工程师并致力于云原生、服务网格等技术领域研究实践,也是Istio 的maintainer的开发者。曾多次在 KubeCon、ServiceMeshCon、IstioCon、GOTC 和 InfoQ/QCon 等大会上发表演讲。

参考文档:

  • [1] https://security.googleblog.com/2022/12/memory-safe-languages-in-android-13.html

  • [2] https://en.wikipedia.org/wiki/Compare-and-swap

  • [3] https://marabos.nl/atomics/hardware.html

  • [4] https://doc.rust-lang.org/reference/inline-assembly.html

  • [5] https://doc.rust-lang.org/std/sync/atomic

  • [6] https://marabos.nl/atomics/memory-ordering.html文章来源地址https://www.toymoban.com/news/detail-807143.html

到了这里,关于一次Rust重写基础软件的实践(一)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 微软用 18 万行 Rust 重写了 Windows 内核

    微软正在使用 Rust 编程语言重写其核心 Windows 库。 5 月 11 日——Azure 首席技术官 Mark Russinovich 表示,最新的 Windows 11 Insider Preview 版本是第一个包含内存安全编程语言 Rust 的版本。 “如果你参加了 Win11 Insider 环,你将在 Windows 内核中首次体验 Rust,”Russinovich 昨晚发推文说。

    2024年02月11日
    浏览(35)
  • Rust 一门赋予每个人构建可靠且高效软件能力的语言

    目录 Rust 安装 尝试 hello, world 编译 链接出错 开启 Rust 之旅 官方教程 《Rust 程序设计语言》 《通过例子学 Rust》 核心文档 标准库 版本指南 CARGO 手册 RUSTDOC 手册 RUSTC 手册 编译错误索引表 非官方翻译教程   Rust 程序设计语言 简体中文版 通过例子学 Rust 中文版  语法基础 变

    2024年02月02日
    浏览(35)
  • Rust语言从入门到入坑——(2)Rust在windows上搭建开发环境

    开始搭建一个适合在windows上运行的Rust环境。 Rust支持的程序语言很多:可详见官网介绍 本文章主要是在windowns下搭建开发环境 首先,需要安装最新版的 Rust 编译工具和 Visual Studio Code。 Rust 编译工具:https://www.rust-lang.org/zh-CN/tools/install Visual Studio Code:https://code.visualstudio.com

    2024年02月09日
    浏览(50)
  • Rust 程序设计语言学习——基础语法

    Rust 语言是一种高效、可靠的通用高级语言。其高效不仅限于开发效率,它的执行效率也是令人称赞的,是一种少有的兼顾开发效率和执行效率的语言。 Rust 语言由 Mozilla 开发,最早发布于 2014 年 9 月。Rust 的编译器是在 MIT License 和 Apache License 2.0 双重协议声明下的免费开源软

    2024年01月22日
    浏览(50)
  • 【跟小嘉学 Rust 编程】三十三、Rust的Web开发框架之一: Actix-Web的基础

    【跟小嘉学 Rust 编程】一、Rust 编程基础 【跟小嘉学 Rust 编程】二、Rust 包管理工具使用 【跟小嘉学 Rust 编程】三、Rust 的基本程序概念 【跟小嘉学 Rust 编程】四、理解 Rust 的所有权概念 【跟小嘉学 Rust 编程】五、使用结构体关联结构化数据 【跟小嘉学 Rust 编程】六、枚举

    2024年02月04日
    浏览(49)
  • 第一次在RUST官方论坛上留言发布我的Rust板箱

    第一次在RUST官方论坛上发帖子,有点紧张~地址在这里: 【My Rust Crate】obtains linux local information - The Rust Programming Language Forum (rust-lang.org)

    2024年01月22日
    浏览(39)
  • Rust安全编码实践 Secure Coding Practices in Rust

    作者:禅与计算机程序设计艺术 Rust编程语言被称为可保证内存安全的系统编程语言,它在编译期间通过类型系统确保数据不出错。因此,Rust语言开发者需要掌握一些安全编码实践,如内存安全、访问控制、输入验证等。本文将对这些安全编码实践进行详细介绍,并结合Rus

    2024年02月04日
    浏览(40)
  • rust实践-异步并发socket通信

    String::from_utf8_lossy() 函数将字节数组转换为字符串 接受一个字节数组作为参数,并尝试将其转换为 UTF-8 编码的字符串 如果字节数组包含无效的 UTF-8 字符,则这些字符将被替换为问号 ? from_utf8_lossy() 函数返回一个 Cow 类型的对象 这个对象实现了 ToString trait 所以可以使用 to_s

    2024年02月13日
    浏览(34)
  • 【rust语言】rust多态实现方式

    学习rust当中遇到了这个问题,记录一下,不对地方望指正 多态是面向对象程序设计中的一个重要概念,指同一个行为或操作在不同实例上具有不同的行为或结果。简单来说,多态就是指同一种类型的对象,在不同的上下文中有不同的行为。多态性使得程序可以更加灵活、可

    2024年02月11日
    浏览(46)
  • # 文盘Rust -- tokio绑定cpu实践

    作者: jiashiwen 原文来源: https://tidb.net/blog/18804515 notice\\\"Rust is a trademark of the Mozilla Foundation in the US and other countries.\\\" tokio 是 rust 生态中流行的异步运行时框架。在实际生产中我们如果希望 tokio 应用程序与特定的 cpu core 绑定该怎么处理呢?这次我们来聊聊这个话题。 首先我们

    2024年02月08日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包