IORingSwift

IORingSwift 是一个轻量级的 Swift 封装库,用于 io_uring。它的设计目标是性能优先,而非可移植性。它并不打算取代 libdispatchSwiftNIO;事实上,它目前需要前者,并且抽象程度也低于后者。

它最初的设计目的是为了支持嵌入式应用中的 SPI,因为 Linux SPI 用户空间驱动是同步的,但它同样擅长处理套接字。导致其开发的讨论可以在这里找到。

该软件包由两个库组成:

其目的是最终也支持 Zephyr 中的实时 I/O 子系统,以便与 SwiftIO 及其封装 AsyncSwiftIO 一起使用。

架构

IORing 操作被隔离到 IORingActor 全局 actor 中。

IORing 通常设计为作为单例 (IORing.shared) 使用,但是由于目前 API 在分配固定缓冲区方面存在一些限制,您可能需要分配单独的实例。目前,所有实例共享相同的 actor 上下文和 io_uring 工作队列,因此分配更多实例没有性能优势。(但这应被视为实现细节。)

Public API 围绕常见操作(如读取和写入)提供结构化并发封装。 多次调用 API,例如 accept(2),它可以随时间返回多个完成,返回 AnyAsyncSequence。 在内部,封装分配一个 Submission<T> 的具体实例,表示一个初始化的 Submission Queue Entry (SQE),然后将其提交给 io_uring。 完成处理程序由 libdispatch 监视一个表示可用完成的 eventfd(2) 来处理。 每个队列条目中的 user_data 是一个块,它在环的隔离上下文中执行 Submission<T> 实例的 onCompletion(cqe:) 方法。 必须小心管理事件生命周期中的指针生命周期。

示例

这是一个 TCP echo 服务器的示例,改编自 IORingTCPEcho

import AsyncExtensions
import IORing
import IORingUtils

let socket = try Socket(ring: IORing.shared, domain: sa_family_t(AF_INET), type: SOCK_STREAM, protocol: 0)
try socket.setReuseAddr()
try socket.setTcpNoDelay()
try socket.bind(port: 10000)
try socket.listen(backlog: 10)

let clients: AnyAsyncSequence<Socket> = try await socket.accept()
for try await client in clients {
    Task {
        repeat {
            let data = try await client.receive(count: bufferSize)
            try await client.send(data)
        } while true
    }
}

更多示例可以在 Examples 中找到。

注释

当然,欢迎提交 pull requests!