Rust for Linux 源码导读 | Ref 引用计数容器
Posted Rust语言中文社区
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Rust for Linux 源码导读 | Ref 引用计数容器相关的知识,希望对你有一定的参考价值。
try_reserve
这几个之前未稳定的特性。和 alloc
。为 alloc
添加了更加模块化的选项,以便禁用一些他们不需要的功能:no_rc
和 no_sync
,主要是为上游 Rust 项目添加。lint
。readX
/writeX
)、irq 芯片和高级流处理程序,gpio 芯片(包括 irq 芯片)、设备、amba 设备和驱动程序以及证书。此外,也改进并简化了 Ref
(refcount_t
支持)对象并用它替换了 Rust 的 Arc
的所有实例。完全地从 alloc
crate 中删除了 Arc
和 Rc
。从现在开始,Rust for linux 团队将开始定期提交补丁,每两周左右。
除了来自 Arm、Google 和 Microsoft 的支持外,这次该团队又收到一封来自红帽的信:红帽对 Rust 用于内核的工作也非常感兴趣(There is interest in using Rust for kernel work that Red Hat is considering)。
Arc
Rust for Linux 中这个 kernel
crate 中之前使用的是 Arc
,但是现在换成了 Ref
。通过查看相关PR ,可以了解其中主要有两点原因:
最大化利用现有的 C
代码 和 消除恐慌(Panic)。内核中已经有了引用计数的实现refcount_t
,而且它超过引用计数的阈值时,不是 Panic(abort) 而是返回最大值(饱和加法)。因为这个原因,也使用 `RBTree`(红黑树)[6] 大小,引用计数加法超过该大小就会溢出然后发生Panic(abort)。所以最终实现的
Ref
与Arc
的区别在于:Ref
是基于内核的refcount_t
来支持的它不支持 弱引用,所以大小减少了一半 当它超过阈值时,它使得引用计数饱和(saturating)而非中止(abort) 它不提供 get_mut
方法,所以引用计数对象是 Pin 的。
总是一个非零的一个实例,并且被
Ref
引用的对象总是 Pin 的(不可移动)。该结构体中使用
NonNull<T>
,而非*mut T
,这里需要协变(covariant),而非不变(invariant)。可以参考下面示例:是
*mut T
的协变版本,并且也代表了非空指针,代表了引用计数对象总是非空的,因为当计数为零就会释放。而这里使用
结构体结构体:PhatomData
则是为了 Drop 检查,此处表示 Ref 类型拥有RefInner<T>
,当Ref
被 Drop 的时候,RefInner<T>
也能跟着被 Drop 。内部包含了内核中 C 语言实现的引用计数结构体
refcount_t
,这里就是为了复用 C 代码。其中 是
kernel
crate 内置的专门为了和 C 打交道提供的一个包装类型,定义如下:结构体定义如下:
API的目标是为实现对象的引用计数器提供一个最小的API。虽然内部使用了原子操作,但一些
refcount_*()
和atomic_*()
函数在内存顺序保证方面有很多不同。refcount_t
在2018年曾经发生过 引用计数溢出的安全漏洞,即,当引用计数达到最大值时,如果再加一,则引用计数就会归零。所以,此时引用的对象就会被错误释放。这样就变成了一个 UAF(use-after-free) 漏洞,容易被人利用。所以现在
refcount_t
被增加了引用计数检测:实现的一些 trait拥有一些类似于
Arc<T>
的行为,所以为其实现一些内置 trait。:是一个未稳定特性(
receiver_trait
features),它表示一个结构体可以作为方法接收者,不需要arbitrary_self_types
特性。标准库中一些智能指针实现了该trait,比如Box<T>
/Arc<T>
/Rc<T>
/&T
/Pin<P>
等。:也是一个未稳定特性( coerce_unsized
features),它表示将 Size 类型转换为 DST 类型。features),它用于对象安全(动态安全 dyn safe)的检查。实现 DispatchFromDyn
的类型可以安全地用作对象安全方法中的 self 类型。Send/Sync
,是Rust 中稳定的特性,用于标记线程间可安全传递和共享的类型。
现在为 Ref<T>
实现了这些 trait,那么 Ref<T>
也就拥有了相应的行为。基本上 Ref<T>
的行为和 Arc<T>
类似了,除了上面所说的那些区别。
比如,Clone
时应该自增引用计数,而 Drop
时应该自减引用计数。所以,分别来看一下这两个实现。
trait 很简单,直接通过 bindings::refcount_inc
来调用内核中 refcount_t
的自增方法 refcount_inc
即可。因为 refcount_inc
已经是有了引用计数溢出检测,使用饱和加法,所以不用担心归零。
trait,同样直接通过 bindings::refcount_dec_and_test
调用内核 refcount_dec_and_test
函数即可,该函数也包含了引用计数溢出检查。但是在引用计数归零的时候,需要释放内存。注意上面 Clone
和 Drop
这两个 trait 的实现,是 Unsafe Rust 抽象为 Safe Rust 的一个经典范例,主要是其中的Safety
注释,考虑了安全边界,并且加以说明。
如何创建新的引用计数对象。方法中使用 core::alloc::Layout
结构体来定义内存布局。通过 NonNull::new
和 自定义的 core::alloc::alloc
函数 来分配新的内存,并转换为 RefInner<T>>
类型,并通过bindings::REFCOUNT_INIT
调用内核 C 函数对其初始化为 1
。其中 自定义的 core::alloc
模块将来都会同步到 rust
core 中。
其中 Error::ENOMEM
代表 OOM
错误。在 中定义了很多内核错误码对应的错误。
Linux 内核中使用整数定义了很多错误码,在 kernel crate 中,使用了 NewType 模式对其进行封装,而非直接使用整数错误码:
构造 Ref<T>
方法中看到,最后一步使用 from_inner
方法将一个裸指针构造为最终的 Ref<T>
。并且它是一个内部方法,不是公开的 API。注意,它是一个 unsafe 的方法,因为需要调用者来确保 inner 的指针是有效且非空的,对于这一点其文档注释也写的比较清楚。
结构体使用 PhantomData<&\'a ()>
来持有生命周期参数,并为其实现 Copy trait,其行为和普通的不可变引用类似。然后为 Ref<T>
实现一个 as_ref_borrow
方法即可从 Ref<T>
得到 RefBorrow<T>
。
改为 as_ref
更好。但是这里其实 as_ref
另有用处:方法从 Ref<T>
得到 &T
。然后为 RefBorrow<T>
实现 Deref
trait,也可以从 RefBorrow<T>
拿到 &T
。
之外,还实现了一个 UniqueRef<T>
类型。顾名思义,该类型表示只有唯一一个引用计数的情况。和 Drop
这两个 trait,所以它只能持有唯一一个引用。引入该类型也许可以为内核开发提供更多便利。还实现了其他 trait,比如 From/TryFrom
,可以从裸指针和 Ref<T>
之间相互转换。一个值得注意的地方是:
转换为裸指针时,注意使用 core::mem::forget(obj)
避免调用 obj
的 Drop ,否则会让引用计数减少而引起问题。类型: https://github.com/Rust-for-Linux/linux/blob/rust/rust/kernel/types.rs#L277[10]core::ops::Receiver
: https://github.com/rust-lang/rust/blob/master/library/core/src/ops/deref.rs#L191
[11]core::ops::CoerceUnsized
: https://github.com/rust-lang/rust/blob/master/library/core/src/ops/unsize.rs#L36
[12]core::ops::DispatchFromDyn
: https://github.com/rust-lang/rust/blob/master/library/core/src/ops/unsize.rs#L117
[13]kernel/error.rs
: https://github.com/Rust-for-Linux/linux/blob/rust/rust/kernel/error.rs#L64
【RUST_BASIC】Rust for Linux环境搭建
参考技术ARust 提供简单的一键安装,命令如下:
rustup 是 Rust 官方的版本管理工具,安装前首先配置国内镜像加速更新工具链:
运行以下命令进行安装:
安装后工具链会被安装到 $HOME/.cargo/bin 目录,.cargo/bin 目录会被添加到系统的 $PATH 环境变量,重新登录后即可使用 rustc,cargo 等命令。
使用国内镜像加速更新 crate 拉取,将如下配置写入 $HOME/.cargo/config 文件:
Rust 有三个 发布通道 (release channel):
使用 nightly 版本:
安装 RLS 组件:
安装 WASM:
安装 racer:
https://rustcc.gitbooks.io/rustprimer/content/install/install_rust_on_linux.html
以上是关于Rust for Linux 源码导读 | Ref 引用计数容器的主要内容,如果未能解决你的问题,请参考以下文章