宣布 tokio-uring:Tokio 的 io-uring 支持

发布"tokio-uring"crate的第一个版本,为Linux上的io-uring系统API提供支持。

今天,我们发布了 “tokio-uring” crate的第一个版本,为Linux上的 io-uring 系统API提供支持。这个版本提供了异步文件操作,我们将在后续版本中增加对更多操作的支持。

要使用 tokio-uring,首先要在 crate 上添加一个依赖项:

tokio-uring = "0.1.0"

然后,启动 tokio-uring 运行时 ,并从文件中读取:

use tokio_uring::fs::File;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    tokio_uring::start(async {
        // Open a file
        let file = File::open("hello.txt").await?;

        let buf = vec![0; 4096];
        // Read some data, the buffer is passed by ownership and
        // submitted to the kernel. When the operation completes,
        // we get the buffer back.
        let (res, buf) = file.read_at(buf, 0).await;
        let n = res?;

        // Display the contents
        println!("{:?}", &buf[..n]);

        Ok(())
    })
}

tokio-uring 运行时在底下使用Tokio运行时,所以它与Tokio类型和库(如hyper和tonic)兼容。下面是和上面一样的例子,但我们不是写到STDOUT,而是写到一个Tokio TCP套接字。

use tokio::io::AsyncWriteExt;
use tokio::net::TcpListener;
use tokio_uring::fs::File;

fn main() {
    tokio_uring::start(async {
        // Start a TCP listener
        let listener = TcpListener::bind("0.0.0.0:8080").await.unwrap();

        // Accept new sockets
        loop {
            let (mut socket, _) = listener.accept().await.unwrap();

            // Spawn a task to send the file back to the socket
            tokio_uring::spawn(async move {
                // Open the file without blocking
                let file = File::open("hello.txt").await.unwrap();
                let mut buf = vec![0; 16 * 1_024];

                // Track the current position in the file;
                let mut pos = 0;

                loop {
                    // Read a chunk
                    let (res, b) = file.read_at(buf, pos).await;
                    let n = res.unwrap();

                    if n == 0 {
                        break;
                    }

                    socket.write_all(&b[..n]).await.unwrap();
                    pos += n as u64;

                    buf = b;
                }
            });
        }
    });
}

所有的 tokio-uring 操作都是真正的异步,与 tokio::fs 提供的API不同,后者在线程池上运行。从线程池中使用同步的文件系统操作会增加大量的开销。有了io-uring,我们可以在同一个线程中异步地执行网络和文件系统操作。但是,io-uring 的内容很多。

Tokio目前的Linux实现使用非阻塞系统调用和 epoll 来进行事件通知。使用 epoll,一个经过调整的TCP代理将花费70%到80%的CPU周期在用户空间之外,包括执行系统调用和在内核和用户空间之间复制数据的周期。Io-uring 通过消除大多数系统调用来减少开销,对于某些操作,提前映射用于字节缓冲区的内存区域。早期将 io-uringepoll 进行比较的基准是有希望的;用C语言实现的TCP echo客户端和服务器显示了高达60%的改进。

最初的 tokio-uring 版本提供了一套适度的API,但我们计划在未来的版本中增加对 io-uring 的所有功能的支持。请看设计文件以了解我们的发展方向。

所以,请尝试一下这个 crate,并随时提出问题或报告问题。

另外,我们要感谢所有在这一过程中提供帮助的人,特别是Glauber Costa(Glommio的作者),他耐心地回答了我的许多问题,withoutboats最初的探索(Ringbahn)和花时间与我讨论设计问题,以及 quininer 在纯Rust `io-uring 绑定上的出色工作。

内容出处: https://tokio.rs/blog/2021-07-tokio-uring