0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Rust重写基础软件的实践代码

jf_wN0SrCdH 来源:Rust语言中文社区 2024-01-19 10:05 次阅读

前言

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

问题描述

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

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

lockcmpxchgq[destination],rdx

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

ldrexr1,[destination]
cmpr1,r2
strexeqr1,r2,[destination]

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

lwarxr0,0,destination
cmpwr0,r1
bneretry;branchifnotequal
stwcx.r2,0,destination
bneretry;branchifstorefailed

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

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

#ifdefined(__i386)||defined(__x86_64__)||defined(__sparcv9)||defined(__sparcv8plus)
typedefunsignedintslock_t;
#else
typedefunsignedcharslock_t;
#endif
externslock_tmy_atomic_cas(volatileslock_t*lock,slock_twith,slock_tcmp);
#defineTAS(a)(my_atomic_cas((a),1,0)!=0)
#endif

对应实现的x86汇编文件 cas_operation.s 的部分代码如下:

my_atomic_cas:
#ifdefined(__amd64)
movl%edx,%eax
lock
cmpxchgl%esi,(%rdi)
#else
movl4(%esp),%edx
movl8(%esp),%ecx
movl12(%esp),%eax
lock
cmpxchgl%ecx,(%edx)
#endif
Ret

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

解决方案

想到两个解决方案:

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

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

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

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

usestd::{AtomicU32,Ordering};

pubtypeuint32=libc::c_uint;
pubstructmy_atomic_uint32{
pubvalue:uint32,
}

implmy_atomic_uint32{
#[inline]
pubfncompare_and_swap(&self,expected:uint32,newval:uint32)->bool{
letatomic_ptr=selfas*constmy_atomic_uint32as*constAtomicU32;
letatomic=unsafe{&*(atomic_ptr)};
atomic.compare_and_swap(expected,newval,Ordering::SeqCst)==expected
}
}

pubfnmy_compare_and_swap_u32_impl(
mutptr:*mutpg_atomic_uint32,
mutexpected:*mutuint32,
mutnewval:uint32,
)->bool{
letatomic=&*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::{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 等大会上发表演讲。

审核编辑:黄飞

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • ARM
    ARM
    +关注

    关注

    134

    文章

    8653

    浏览量

    361825
  • Android
    +关注

    关注

    12

    文章

    3851

    浏览量

    125642
  • C语言
    +关注

    关注

    180

    文章

    7533

    浏览量

    128782
  • 汇编代码
    +关注

    关注

    0

    文章

    23

    浏览量

    7512
  • Rust
    +关注

    关注

    1

    文章

    223

    浏览量

    6387

原文标题:一次Rust重写基础软件的实践(一)

文章出处:【微信号:Rust语言中文社区,微信公众号:Rust语言中文社区】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Rust GUI实践Rust-Qt模块

    Rust-Qt 是 Rust 语言的一个 Qt 绑定库,它允许 Rust 开发者使用 Qt 框架来创建跨平台的图形界面应用程序。Qt 是一个跨平台的应用程序框架,它提供了一系列的工具和库,可以帮助
    的头像 发表于 09-30 16:43 965次阅读

    Rust的 match 语句用法

    执行不同的代码,这在处理复杂的逻辑时非常有用。在本教程中,我们将深入了解 Rust 的 match 语句,包括基础用法、进阶用法和实践经验等方面。 基础用法 match 语句是 Rust
    的头像 发表于 09-19 17:08 657次阅读

    Rust的多线程编程概念和使用方法

    和字段、常见用法以及多线程的一些实践经验。由浅入深带你零基础玩转Rust的多线程编程。 线程的基本概念和使用方法 Thread是Rust中并发编程的一种基本方式。Rust中的Threa
    的头像 发表于 09-20 11:15 508次阅读

    如何编写高性能的Rust代码

    为了最大限度地提高Rust应用程序的性能,你需要了解支持代码的底层硬件架构,如何优化算法和数据结构,以及如何对代码进行配置和基准测试。在本文中,我们将简要介绍这些主题,希望能更好地理解如何编写高性能的
    的头像 发表于 11-03 14:28 481次阅读
    如何编写高性能的<b class='flag-5'>Rust</b><b class='flag-5'>代码</b>

    LabVIEW 助力优化软件开发实践和源代码控制

    软件开发实践和程序源代码控制管理。  源代码控制管理  源代码控制管理是软件工程最基本的环节之
    发表于 09-13 09:30

    Rust代码中加载静态库时,出现错误 ` rust-lld: error: undefined symbol: malloc `怎么解决?

    “ [i]malloc ”、“ [i]exit ”。我验证了使用 ` [i]nm ` 命令。 问题是我打算使用 ffi 在 rust 中使用这个静态库。当我尝试在我的 Rust 代码中加载静态库
    发表于 06-09 08:44

    微软开发基于Rust的新编程语言,将很快开源

    此前,微软表示正探索将Rust作为C和C++的安全替代方案,并且也对外展示了使用Rust重写Windows组件的体验,根据微软的说法,Rust是一种从根本上考虑安全性的编程语言,他们将
    的头像 发表于 12-03 10:36 3734次阅读

    Cloudflare用Rust重写Nginx C模块,构建没有Nginx的未来

    近日,Cloudflare 工程师介绍了如何使用 Rust 重写基于 C 语言的 Nginx 模块。Cloudflare 工程师在博客写道,他们用 Rust 为 Cloudflare 基础设施中最
    的头像 发表于 03-08 09:36 472次阅读

    如何在同步的Rust方法中调用异步代码呢?

    在同步的 Rust 方法中调用异步代码经常会导致一些问题,特别是对于不熟悉异步 Rust runtime 底层原理的初学者。
    的头像 发表于 03-17 09:18 1546次阅读

    Rust重写的LSP:KCL IDE 插件的功能介绍与设计解析

    在这次更新中,我们发布了全新的 KCL VS Code 插件,并且用 Rust 重写了 LSP 的 Server 端。我们提供了 IDE 中常用的代码辅助功能,如高亮、跳转、补全、Outline、悬停、错误提示等。
    的头像 发表于 05-11 09:39 609次阅读
    <b class='flag-5'>Rust</b><b class='flag-5'>重写</b>的LSP:KCL IDE 插件的功能介绍与设计解析

    Windows 11初尝Rust,36000行内核代码重写

    更早些时候,微软用 Rust 重写了 DirectWrite Core 库的概念验证,它是 Windows 的 DWrite 引擎的 Windows App SDK 实现,用于文本分析、布局和渲染
    的头像 发表于 05-19 16:39 674次阅读
    Windows 11初尝<b class='flag-5'>Rust</b>,36000行内核<b class='flag-5'>代码</b>已<b class='flag-5'>重写</b>!

    rust语言基础学习: rust中的错误处理

    错误是软件中不可避免的,所以 Rust 有一些处理出错情况的特性。在许多情况下,Rust 要求你承认错误的可能性,并在你的代码编译前采取一些行动。
    的头像 发表于 05-22 16:28 1404次阅读

    Rust的内部工作原理

    Rust到汇编:了解 Rust 的内部工作原理 非常好的Rust系列文章,通过生成的汇编代码,让你了解很多Rust内部的工作机制。例如文章有
    的头像 发表于 06-14 10:34 468次阅读
    <b class='flag-5'>Rust</b>的内部工作原理

    如何在同步的 Rust 方法中调用异步代码 | Tokio 使用中的几点教训

    在同步的 Rust 方法中调用异步代码经常会导致一些问题,特别是对于不熟悉异步 Rust runtime 底层原理的初学者。
    的头像 发表于 12-24 16:23 583次阅读

    一次Rust重写基础软件实践

    受到2022年“谷歌使用Rust重写Android系统且所有Rust代码的内存安全漏洞为零” [1] 的启发,最近笔者怀着浓厚的兴趣也顺应Rust
    的头像 发表于 01-25 11:21 273次阅读