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

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

3天内不再提示

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

jf_wN0SrCdH 来源:Rust语言中文社区 2024-01-25 11:21 次阅读

前言

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

问题描述

本文将记录转化过程中遇到的另外一个问题。该问题是由已经转化完成的 Rust 代码使用到软件中引入的第三方软件包和链接库所导致的。设想这样一个场景:Rust 项目中完成某一个功能点需要用到一个或多个第三方软件包和链接库。这显然是很常见的用户场景,但是由于用户环境不同,用户安装的第三方软件包和链接库的版本不同,使得转化后的 Rust 代码必须要做适当的兼容处理。

这里所说的用户的环境不同,可以理解为芯片指令集的平台不同,如 Intel x86 以及国产的 ARM 麒麟服务器。当然更常见的情形是芯片平台相同,但是存在操作系统层面第三方软件包和链接库安装的差异,如 x86 下的 Ubuntu 和 CentOS 中用户安装了不同版本的第三方软件包和链接库等。

事实上,即使排除所有平台和系统层面的差异,由于用户安装了该基础软件所依赖的不同版本的第三方软件包和链接库,然而这些第三方软件包或者链接库由于自身的演进导致不同版本之间存在较大差异(可能实现相同功能的函数和函数签名都有千差万别),这给我重写该软件的工作带来了一些挑战。基于上述说明,在完成重写该基础软件的过程中如何使得转化后的 Rust 代码能兼容该基础软件所依赖的主流第三方软件包和链接库则是我遇到的最大挑战。需要说明的是这里的第三方软件包和链接库可能是基于 Rust 语言开发的,也可能是基于 C 语言开发的。

解决方案

对于此问题的解决方案需要使用 Rust FFI(Foreign Function Interface) [1],这基本上是没有太大争议的。因为在本次软件重写过程中我遇到的场景是:对于不同版本的链接库使用哪个版本的函数取决于用户的安装运行时环境,所以除了 Rust FFI,在代码适配上我还考虑了使用 Rust features [2] 机制。

下面我简化了一下场景和解决方案,同时我把样本代码放到了我的 github [3] 里,欢迎大家一起交流。如样本代码所示,my-rust-bin 文件夹中的一段业务代码需要调用到静态链接库 my_rust_lib 中的函数,该链接库有两个版本 v1(在文件夹 my-rust-lib-v1 中) 和 v2(在文件夹 my-rust-lib-v2 中), 且不同版本的库其函数不一样。

my-rust-lib-v1 对应的业务函数为:pub fn my_rust_lib_v1(left: usize, right: usize) -> usize

my-rust-lib-v2 对应的业务函数为:pub fn my_rust_lib_v2(left: usize, right: usize) -> usize

另外一个 lib 文件夹的目的其实是为了模拟用户本地安装的链接库。可以分别编译不同版本的静态链接库,然后把生成的库文件(在本例中是)libmy_rust_lib.a, 然后把不同版本的库文件拷贝到此文件夹下,以此来模拟用户环境中安装的不同版本的链接库。解决方案中的关键点在于 my-rust-bin 中,

首先在 my-rust-bin 的 Cargo.toml 中有定义对应的 features,如下所示:

[features]
v1=[]
v2=[]

其次在 my-rust-bin 的 src/main.rs 下的代码如下:

#[cfg(feature="v1")]
modbindingmylib{
extern"C"{
pubfnmy_rust_lib_v1(left:usize,right:usize)->usize;
}
}

#[cfg(feature="v2")]
modbindingmylib{
extern"C"{
pubfnmy_rust_lib_v2(left:usize,right:usize)->usize;
}
}

#[cfg(not(any(feature="v1",feature="v2")))]
compile_error!("Pleasespecifyeither'v1'or'v2'feature");

pubfnmy_rust_lib(left:usize,right:usize)->usize{
#[cfg(feature="v1")]
unsafe{
returnbindingmylib::my_rust_lib_v1(left,right);
}

#[cfg(feature="v2")]
unsafe{
returnbindingmylib::my_rust_lib_v2(left,right);
}
}

fnmain(){
letr_value:usize=my_rust_lib(3,5);
println!("Thereturnvalueofmy_rust_libis[{}]",r_value);
}

现在我来解读一下这段代码。代码先分别定义一个相同的模块 bindingmylib,然后根据 features 分别引入的依赖,使用的不同的静态链接库函数(my_rust_lib_v1 和 my_rust_lib_v2), 同时通过 compile_error! 定义一个没有设置 v1 和 v2 features 的编译错误(防止编译时忘记设置 features选项,下面在编译环节的时候有用)。最后将两个有差异的函数统一为函数 my_rust_lib,并在该函数中根据 features 定义分别调用不同的函数并返回相应的值。

最后是在 my-rust-bin 中编译二进制文件:

编译并运行 v1 的二进制文件

#编译v1版本的my-rust-bin
$cdmy-rust-bin
$cargobuild--features="v1"

#运行v1版本的my-rust-bin
$target/debug/my-rust-bin
my_rust_lib_v1:8
Thereturnvalueofmy_rust_libis[8]

编译并运行 v2 的二进制文件

#编译v2版本的my-rust-bin
$cdmy-rust-bin
$cargobuild--features="v2"

#运行v2版本的my-rust-bin
$target/debug/my-rust-bin
my_rust_lib_v2:8
Thereturnvalueofmy_rust_libis[8]

备注:如果编译的时候没有设置 --features 则会有如下输出:

$cargobuild
error:Pleasespecifyeither'v1'or'v2'feature
-->src/main.rs1
|
16|compile_error!("Pleasespecifyeither'v1'or'v2'feature");
|^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

至此,用户在编译好该基础软件之后,就可以无感知的通过统一的函数入口调用不同版本的相同链接库中的不同函数了。

总结

本文主要是在简化了问题的实际场景以后,解决不同版本的同一软件包或者链接库中,函数及其函数签名不同导致的调用问题。之所以说简化,主要是本文所描述的场景中,my-rust-bin 和其依赖的外部链接库均是 Rust 编写。而在我的实际场景中则会更复杂一些,存在着 Rust 代码依赖 C 编写的外部链接库,同时存在混合的原来 C 代码部分依赖新改写的 Rust 外部链接库的情况。但是无论哪种情况,万变不离其宗,我们都可以从这种最简单的场景出发去解决遇到的问题。

关于作者

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

审核编辑:汤梓红

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

    关注

    37

    文章

    6290

    浏览量

    121898
  • C语言
    +关注

    关注

    180

    文章

    7534

    浏览量

    128838
  • 代码
    +关注

    关注

    30

    文章

    4556

    浏览量

    66814
  • Rust
    +关注

    关注

    1

    文章

    223

    浏览量

    6387

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

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

收藏 人收藏

    评论

    相关推荐

    Rust GUI实践Rust-Qt模块

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

    Rust的 match 语句用法

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

    TC397软件复位是只能执行一次吗?

    TC297软件复位可以正常使用,TC397的执行软件复位后只能正常复位一次,然后就直处于复位状态,程序不再运行,这是什么原因,还有什么其他操作设置吗
    发表于 02-05 07:16

    使用HAProxy软件一次学习过程介绍

    一次学习过程
    发表于 06-13 10:48

    只会用Python?教你在树莓派上开始使用Rust

    以及是否存在所有依赖项的功能。cargo check您的连接可能需要花费几分钟,但是当您在Cargo.toml文件中添加或更改项目时,只需要做一次。Hello Blink现在,您将把Hello
    发表于 05-20 08:00

    AVR Studio软件为什么只有第一次能连上单片机

    每次打开AVR Studio 软件。只有第一次能连上单片机,用过一次后,就连不上了。出现 Could not connect to JTAGICE mkII on USB or port COM1 to COM4 的提示。该怎么
    发表于 07-13 06:42

    如何用 rust 语言开发 stm32

    注意的是该款芯片为国产仿品,在烧录的时候需要对软件进行定修改。仿真器选用 STLINK V2。可以选图片上的低配版,也可以选白色 20 针的较高级版。连接时只需要按板子上的标注把相同的引脚连起来即可。此外需要至少四根母对母杜邦线。软件
    发表于 11-26 06:20

    RUST在嵌入式开发中的应用是什么

    Rust种编程语言,它使用户能够构建可靠、高效的软件,尤其是用于嵌入式开发的软件。它的特点是:高性能:Rust具有惊人的速度和高内存利用
    发表于 12-24 08:34

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

    我正在 MCUXpresso IDE 中创建个静态库。我正在使用 redlib 在我的代码中导入 ` [i]stdlib.h`。它成功地构建了个静态库。但是,静态库中未定义些标准库函数,例如
    发表于 06-09 08:44

    HarmonyOS\"一次开发,多端部署\"优秀实践——玩机技巧,码上起航

    的潜在用户群体。个应用要在多类设备上提供统的内容,需要适配不同的屏幕尺寸和硬件,开发成本较高。\"一次开发,多端部署\"(后文中简称为\"多\")的应用开发
    发表于 08-30 18:14

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

    使用Rust重写各种产品,因为在过去的十年里,微软70%以上的安全补丁都提供了与内存相关的错误,而Rust正是解决这个问题的良药。 而根据ZDNet的报导,近日在一次演讲中,谈到微软为
    的头像 发表于 12-03 10:36 3734次阅读

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

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

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

    在这次更新中,我们发布了全新的 KCL VS Code 插件,并且用 Rust 重写了 LSP 的 Server 端。我们提供了 IDE 中常用的代码辅助功能,如高亮、跳转、补全、Outline、悬停、错误提示等。
    的头像 发表于 05-11 09:39 610次阅读
    <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 675次阅读
    Windows 11初尝<b class='flag-5'>Rust</b>,36000行内核代码已<b class='flag-5'>重写</b>!

    Rust重写基础软件实践代码

    在项目转化过程中我遇到了一个与 CAS (Compare and Swap) [2] 操作实现相关的问题,在计算机科学中CAS 是多线程/协程中用于实现同步的原子指令。
    的头像 发表于 01-19 10:05 201次阅读