Ringbahn: a safe, ergonomic API for io-uring in Rust

栏目: IT技术 · 发布时间: 4年前

内容简介:Inmy previous post, I discussed the new io-uring interface for Linux, and how to create a safe API for using io-uring from Rust. In the time since that post, I have implemented a prototype of such an API. The crate is calledio-uring is going to be majorly

Inmy previous post, I discussed the new io-uring interface for Linux, and how to create a safe API for using io-uring from Rust. In the time since that post, I have implemented a prototype of such an API. The crate is called ringbahn , and it is intended to enable users to perform IO on io-uring without any risk of memory unsafety.

io-uring is going to be majorly important to the development of async IO on Linux in the future, and Linux is the most used platform for the kinds of high performance network services that are a major user of Rust. Rust needs to not only have a solution for using io-uring, but should also have the best solution. Users should be able to easily access the full power of io-uring from ergonomic, memory safe, well abstracted APIs. ringbahn is not production ready, but it is a step toward that goal.

Here’s a code sample, showing how easy it can be to perform IO on ringbahn:

use std::fs::File;
use futures::io::AsyncRead;

use ringbahn::Ring;

async fn read_on_ringbahn(file: File, buf: &mut [u8]) -> io::Result<usize> {
    // the `Ring` adapter takes a std IO object and runs its IO on io-uring
    let mut file: Ring<File> = Ring::new(file);
    file.read(buf).await
}

I hope to write a few posts documenting the internal structure and design of ringbahn.

Events, drivers, and submissions

Ringbahn’s core concept is the Submission future, which allows users to safely run an IO event on an io-uring instance managed by a driver. Submission provides a contract between two types:

  • Events: are IO events (like reads and writes) which can be scheduled on an io-uring instance.
  • Drivers: implement the Drive trait and manage an io-uring instance onto which events can be scheduled.

The Submission type handles the exchange between discrete IO events and the driver that manages the io-uring instance so that the interface is memory safe and straight forward. Once the driver has proffered up space to submit an event, the Submission future manages submitting the event and preparing an internal Completion to wake itself when the event completes.

What is a driver?

One of the most important innovations in ringbahn is the notion of drivers, which manage an io-uring instance. Rather than simply providing an interface to io-uring, ringbahn actually allows end users to replace the default driver and take low-level control over how their IO is scheduled. A driver is responsible for three things:

  • Readying a spot in the submission queue to submit an event. Drivers can apply backpressure if there is not space to submit another event.
  • Submitting all of the prepared events in the submission queue. Again, drivers can apply backpressure here if needed.
  • Processing all of the events in the completion queue so that they will be awoken by passing them to the complete function.

There are many different patterns for implementing drivers and determining how they actually submit events and process their completions. By making this an abstraction point, users will be able to experiment with different driver patterns and benchmark how they perform under different kinds of loads, without rewriting all of the memory-safety aspects themselves.

The current version of ringbahn contains a demo driver which is suitable for testing purposes, but is not the most performant possible implementation. In a future post I’ll explore different designs for driving io-uring and how they could be implemented.

The event contract and ownership lifecycle

On the other side of the contract are event types, which represent a single IO event to be scheduled on the io-uring. The Event trait has an interesting quirk: it contains an unsafe method, Event::prepare .

It’s important to understand what an unsafe methods means : unsafe methods are unsafe to call - meaning that callers must guarantee additional invariants when calling them. However, they are safe to implement (as long as you don’t do anything unsafe inside of them). So when a user implements the Event trait, they are given additional guarantees about how this method will be called that normally they could not assume.

Specifically, they are given a guarantee that after this method is called, the event type will not be accessed again until after the event that’s been prepared is completed or cancelled. In other words, it acts as if Event::prepare “passes ownership” of the event to the kernel. That way, implementers of Event can safely give the kernel ownership of buffers without worrying about them being accessed by other parts of the program.

The Event API also supports a cancellation API, which constructs a “cancellation callback.” At a high level, the concept is this: if a user cancels interest in an event, the cancellation callback is constructed. When the event completes, instead of waking the user’s task (which no longer cares about this event), the cancellation callback is called to clean up any objects the kernel had taken ownership of.

What this means is that the Event API provides a memory-safe abstraction for scheduling IO events which fully supports cancellation without leaking any resources.

The Ring interface and buffer management

The most high-level interface in ringbahn is the Ring type . Inspired by the Async type from smol , it wraps a standard IO object and performs IO on it using io-uring, instead of blocking IO. The Ring type acts like a type that repeatedly submits events to its io-uring driver (internally, it doesn’t use the Submission and Event APIs directly, but the code mirrors those APIs fundamentaly). Ring implements AsyncRead , AsyncWrite , and AsyncBufRead .

Currently, the Ring type submits events using its own managed buffers. However, in the longer term Ring would ideally be abstract over multiple different kinds of buffer management strategies - especially including ones which pre-register buffers with the kernel, which would be the most optimal approach.

Ring will provide a simple and straightforward way to perform async IO using io-uring, and will always be the most accessible, high level API. Users can choose between that and constructing more complex IO patterns using the lower level Event API.

Next steps

Ringbahn is currently just a prototype, which is why I’ve released it to crates.io as 0.0.0-experimental.1 . It contains a lot of unsafe code which has not been sufficiently audited or hardened, and users should not use it in production yet .

In the immediate future, I want to blog in more depth about every aspect of ringbahn’s design. I hope this will help steer the future of Rust’s io-uring support in a productive direction. Expect to see a series of posts on the completion state machine, the driver interface, buffer management, and so forth over the next few weeks.

In the longer term, I would like for there to exist a production ready, best-in-class io-uring interface in Rust. However, I don’t know how much time I will be able to commit to this project, or whether it will continue to be developed over the long term. If anyone is interested in funding this work on a serious basis, I would be interested in hearing from them privately.


以上所述就是小编给大家介绍的《Ringbahn: a safe, ergonomic API for io-uring in Rust》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

虚拟经济学

虚拟经济学

威利•莱顿维塔、爱德华•卡斯特罗诺瓦 / 崔毅 / 中国人民大学出版社 / 2015-6 / 49.00元

电子游戏中也存在 “看不见的手”吗?玩虚拟游戏能够创造真实价值吗?为什么现实世界需要虚拟经济?经济学作为一门成熟的学科,起源于对农业、制造业和商业的探究,曾经作为解决饥饿、就业这些人类所面对的真实问题的方法。然而,在虚拟世界,最为稀缺的资源不再是食物和住所,而是人类的关注度。一些基于农业、制造业和商业存在的经济学理论、概念依然适用于游戏中的虚拟世界,比如最为人们所熟知的“看不见的手”这一概念。同时......一起来看看 《虚拟经济学》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具