关于linux:Linux-全新异步接口-iouring-的-Rust-生态盘点

7次阅读

共计 3742 个字符,预计需要花费 10 分钟才能阅读完成。

首发|RustMagazine

io_uring 半信半疑是近两年内核圈最火的话题之一,作为风头正劲的 Linux 异步 I/O 接口,其野心更大,不仅仅想将 Linux 的 I/O 操作全面异步化,还心愿将所有Linux 零碎调用异步化

Rust 作为一门零碎级编程语言,兼具平安和高性能的特点,大家也肯定是想应用 Rust 语言“尝鲜”io_uring。然而遗憾的是 io_uring 作者 Jens Axboe 仅仅保护一个 C 语言的库。用户想要用 Rust 调用,一方面还须要本人进行一些封装,另一方面 C 语言的接口还是太底层,想在 Rust 的异步框架中应用仍有许多工作要做。

好消息是曾经有一些 Rust 语言封装的 io_uring 库呈现在 github 上,明天让咱们来筛选一些应用人数较多(通过 star 数目来判断)的库进行剖析,看看是否能够给大家应用 io_uring 带来便当。

Tokio io-uring

Tokio 是 github 上 Star 数目最多的异步框架,那么他们团队封装的 io_uring lib 如何呢?通过浏览代码不难发现,该 io_uring 库齐全撇弃了 C 语言的 liburing 库,本人在 io_uring 零碎调用上从零开始封装了一层,实现了 submission queue,completion queue 和 submitter。

上述的三层形象比 C 语言的封装略微高层一些,但依然需用户将 request 放到 submission queue 上,将 response 从 completion queue 上取下,和同步读写形式区别微小,且和 Rust 现有的异步 I/O 框架的设计相去甚远。以下是一个简略的样例代码:

let mut ring = IoUring::new(256)?;
let (submitter, mut sq, mut cq) = ring.split();
​
let mut accept = AcceptCount::new(listener.as_raw_fd(), token_alloc.insert(Token::Accept), 3);
​
// put request on the submission queue
accept.push_to(&mut sq);
​
// submit the request
match submitter.submit_and_wait(1) {Ok(_) => (),
    Err(ref err) if err.raw_os_error() == Some(libc::EBUSY) => (),
    Err(err) => return Err(err.into()),
}
​
// get complete events from the completion queue
for cqe in &mut cq {...}


该 io_uring 库的优缺点分列如下:
长处:
1、纯 Rust 封装,安全性更好。
2、比 C 语言库封装高层,应用起来接口更加简略。
毛病:
1、保护老本更高,须要依据 kernel 的更新手动追加新 feature,包含新数据结构。
2、封装还不够彻底,裸露了底层实现的两个队列,用户应用难度较高。

Spacejam rio

该 io_uring 库在 github 上的 star 数目在写稿时曾经达到了 590 个,该库的作者还创立了 sled 嵌入式数据库。因为 sled 数据库也应用了这个 io_uring 库,所以咱们有理由置信,rio 是一个通过理论我的项目验证的库,其更敌对的用户接口更是升高了用户的应用难度。

通过上面的简略示例,大家能够很容易感触到接口的易用性:

/// Read file example
let ring = rio::new().expect("create uring");
let file = std::fs::open("file").expect("openat");
let data: &mut [u8] = &mut [0; 66];
let completion = ring.read_at(&file, &mut data, at);

// if using threads
completion.wait()?;

// if using async
completion.await?

rio 同时提供了针对 thread 和 async 两种编程模型的接口,在提供便利性的同时大大降低了使用者的束缚,能够自由选择喜爱的编程模型。然而这个库是 unsoundness 的,即有可能被谬误或者歹意应用。并且依据作者在 issue 外面的回复,作者并不会对此进行修复。这将使得基于该库构建的软件都不平安。

该 io_uring 库的优缺点分列如下:
长处:
1、接口丰盛且应用简略。
2、有理论应用的我的项目验证。
毛病:
1、Unsoundness,安全性不佳。

ringbahn

ringbahn 的作者是 withoutboats, Rust 语言的外围开发者之一。该库由三个形象层组成,第一层为 C 语言 libfuse 的 Rust 封装, 名称为 uring-sys;第二层为 Submission Queue 和 Completion Queue 等数据结构的封装,名称为 iou;最初一层则封装了 Rust 异步编程的接口。不难看出,ringbahn 从设计上思考了更多,从接口易用性到安全性都更加优良。以下为拷贝文件的示例:

/// Copy File from props.txt to test.txt
futures::executor::block_on(async move {let mut input:  File = File::open("props.txt").await.unwrap();
    let mut output: File = File::create("test.txt").await.unwrap();
    let mut buf = vec![0; 1024];
    let len = input.read(&mut buf).await.unwrap();
    output.write(&mut buf[0..len]).await.unwrap();
    output.flush().await.unwrap();
});
​

该库也并非白璧无瑕,它也具备下列缺点:
1、并发不敌对,在 Submission Queue 上有一把大锁,每个提交工作的线程都会被串行化。
2、读写操作会导致内存在用户态被拷贝,对于大数据量的操作而言,多余的内存拷贝会带来显著的性能降落。之所以要进行内存拷贝,是为了保障传给内核的 memory buffer 不会被用户态异步批改,保障安全性。

作者也在 Readme 文件中阐明了最上层的 ringbahn 封装只是一次尝试,并不适宜在正式生产上应用。

DatenLord ring-io

基于上述探讨,咱们团队 Datenlord 也实现了本人的 io_uring Rust lib,名称是 ring-io。现阶段的实现汲取了 Tokio io-uring 和 iou 的教训,同样实现了 Submission Queue 和 Completion Queue 的形象。具体的实现细节请参见王徐旸同学写的文章。

现阶段的实现也具备下列问题:
1、裸露了一些 unsafe 接口,揭示用户某些操作须要留神,和内核的谬误交互会带来无奈预知的后果。
2、形象层偏低,应用起来不不便。

接下去,咱们会针对一些特定的 buffer 类型实现异步 I/O 接口,不便用户的应用,且裸露 safe 的接口。在实现的过程中,咱们也会将高效思考在内,防止不必要的内存拷贝。和 ringbahn 的办法不同,咱们保障内存平安的形式为 Rust 提供的内存所有权转移,即用户在发送 I/O 申请之后就不在领有 buffer 的所有权,直到 request 返回所有权才被偿还。具体的实现细节咱们会在下一篇文章中进行探讨,这里先给出设计的架构图:

  • SQ submitter 负责将用户 Task 发送来的 I/O 申请通过 io_uring 发送到 kernel。
  • CQ collector 负责将 kernel 实现工作的返回后果返回给用户。
  • User Task 会 block 在各自的 channel 上,直到 I/O 工作实现,User Task 才会被从新调度。

总结

尽管 io_uring 十分火爆,国内外也有很多团队进行了 Rust 封装,然而依然没有一个完满的计划,同时解决了安全性、高性能和易用性的问题。大家能够依据本人的状况抉择一个合乎需要的库,当然更心愿大家踊跃奉献社区,提出本人的想法,创立出更好用、更平安和更快的 io_uring 库。

DatenLord

DatenLord 是用 Rust 实现的 新一代开源分布式存储,面向云原生场景提供高性能存储解决方案。

一方面,在当今的硬件架构下,CPU 和 GPU 的计算的速度远远超过 IO 的速度,即使当初 NVMe SSD 的 IO 速度曾经比从前机械硬盘的速度有了百倍的晋升,网络的速度也有至多百倍晋升,但还是经常碰到 IO 跟不上计算速度的问题,导致计算期待数据,升高了计算的性能。

另一方面,操作系统的 IO 模型曾经很久没有产生大的变动,依然是以内核为主体来执行 IO 工作,这样的形式带来不少额定的开销,诸如数据拷贝、零碎调用引起的阻塞以及过程上下文切换等等。

为了进步 IO 性能,DatenLord 采纳绕过内核 (bypass Kernel) 的形式,次要在用户态实现 IO 性能,防止内核执行 IO 工作带来的额定开销,从而实现高性能分布式存储。

举荐浏览

io_uring Rust 异步库实现办法

正文完
 0