gh-liu / myNote

0 stars 0 forks source link

`linux`中的`io_uring` #10

Open gh-liu opened 4 months ago

gh-liu commented 4 months ago

Linux I/O 的演进

最开始是同步(阻塞式)系统调用 随着实际需求和具体场景,不断加入新的异步接口,还要保持与老接口的兼容和协同工作(在非阻塞式读写的问题上并没有形成统一方案) 到 io_uring 的出现

  1. 阻塞式:基于文件描述符fd的系统调用,fd指向文件/网络套接字
  2. 非阻塞式+轮询:只支持网络套接字/管道,部分不支持文件
  3. 线程池:主线程分发I/O,工作线程进行阻塞调用
  4. 直接I/O访问:指定O_DIRECTflag,零拷贝I/O
  5. 异步I/O linux-aio:只支持 O_DIRECT 文件,对非数据库应用基本无用;拓展很复杂;可能导致阻塞
  6. io_uring

io_uring

Linux内核(5.1)提供的新异步I/O框架

原生 Linux AIO 框架存在各种限制,io_uring 旨在克服这些限制:

  1. 原生AIO不支持缓冲 I/O,仅支持直接 I/O
  2. 原生AIO具有不确定性行为,可能在各种情况下阻塞
  3. 原生AIO有一个不理想的 API,每个 I/O 至少需要两次系统调用,一次用于提交请求,一次用于等待其完成

io_uring实例有两个环:提交任务队列SQ完成任务队列CQ,被内核和用户程序共享; 这两个队列都是单生产者、单消费者,大小为2的幂;

  1. 用户程序创建一个或多个提交任务项SQE,更新提交任务队列SQ队尾;
  2. 内核消费提交任务项SQE,更新提交任务队列SQ队头;
  3. 内核创建完成任务项CQE,更新完成任务队列CQ队尾;
  4. 用户程序消费完成任务项CQE,更新完成任务队列CQ队头;

API

io_uring_setup io_uring_register io_uring_enter

// 设置好`提交队列`和`完成队列`,至少`entries`个项
// 返回的 fd 用于执行后续操作
// 通过`io_uring_params`可以配置三种模式:Interrupt driven, Polled, Kernel polled
// 
// setup a context for performing asynchronous I/O
int io_uring_setup(u32 entries, struct io_uring_params *p);

// 注册文件或用户缓冲区允许:
// 1. 内核长期引用与文件关联的内部内核数据结构,
// 2. 内核创建与缓冲区关联的应用程序内存的长期映射,
// 仅在注册期间而不是在处理每个 I/O 期间进行一次请求,从而减少每个 I/O 开销。
// 根据不同的 opcode 决定不同的 arg 
//
// register files or user buffers for asynchronous I/O
int io_uring_register(unsigned int fd, unsigned int opcode, void *arg, unsigned int nr_args);

// 使用共享的SQ和CQ(由io_uring_setup创建)进行初始化或完成I/O操作
// 单次调用`io_uring_enter`可以同时:1. 提交新的I/O操作 2. 等待上一次调用`io_uring_enter`提交的I/O完成
//
// initiate and/or complete asynchronous I/O
int io_uring_enter(unsigned int fd, unsigned int to_submit, unsigned int min_complete, unsigned int flags, sigset_t *sig);