当前位置: 首页 > news >正文

io_uring最简单的实例io_uring-test.c分析

文章目录

  • 1. 背景知识
    • 1.1 io_uring的3个系统调用
    • 1.2 io_uring封装库liburing
  • 2. 应用io_uring-test.c示例分析
    • 2.1 下载地址及编译方法
    • 2.2 代码分析
      • 2.2.1 io_uring_queue_init API
      • 2.2.2 io_uring_get_sqe API
      • 2.2.3 io_uring_prep_readv API
      • 2.2.4 io_uring_submit API
      • 2.2.5 io_uring_wait_cqe API
      • 2.2.5 io_uring_queue_exit函数
  • 3. io_uring性能提升关键
    • 3.1 队列映射共享
    • 3.2 队列无锁操作
    • 3.3 支持缓存注册
  • 4 参考

在这里插入图片描述

1. 背景知识

1.1 io_uring的3个系统调用

io_uring提供了3个系统调用函数:io_uring_setup,io_uring_enter,io_uring_register。

  • io_uring_setup:创建内核上下文(下图io_ring_ctx),分配一个关联内核上下文(private指针指向io_ring_ctx)的文件,返回其句柄。内核上下文的核心是创建(分配)了装载IO执行实体SQE、IO返回实体CQE及其环构成者指针sq、cq。
    在这里插入图片描述

  • io_uring_enter:是一个通知信使。应用要提交了,可由它通知内核处理提交;应用要结果了,可由它问内核是否有足够的数量的数据;它还可以同时进行通知提交并索要一定数量的结果。在等待结果时可以指定一定超时时间。

  • io_uring_register:case比较多,典型的应用是注册buffer和文件句柄,以注册buffer为例,所谓注册就是将用户申请的用于IO处理的buffer在内存管理时加上pin标志,一是防止被交换,二是后续使用不需要再次映射到内核,减少操作,使用方法是直接使用READ_FIXED/WRITE_FIXED和buffer index指代注册的buffer。

1.2 io_uring封装库liburing

应用要使用以上3个系统调用完成IO还是有点复杂,为简化使用,提供了对其封装的liburing库:

  • io_uring_queue_init:调系统调用io_uring_setup和mmap完成io_uring在内核和用户的初始化。

  • io_uring_get_sqe:取一个可用的sqe,不涉及系统调用。

  • io_uring_prep_*系列函数:将IO参数直接赋值sqe的成员,prepare的意思即是准备好了sqe,如下具体接口只简单列了文件类和网络类:

    • io_uring_prep_read、io_uring_prep_write:文件类。
    • io_uring_prep_accept、io_uring_prep_send:网络类。
  • io_uring_submit:将准备好的sqe提交给内核,触发内核执行操作,内部依赖io_uring_enter系统调用。

  • io_uring_wait_cqe:阻塞等待至少一个操作完成,并返回完成的CQE,这一步是阻塞的;不阻塞的接口是io_uring_peek_batch_cqe,批量获取完成队列中的操作结果, 返回值表示已经完成的操作数量。

  • io_uring_cqe_seen:调的是io_uring_cq_advance,通知内核该cqe已被处理,它是空闲可用的。

2. 应用io_uring-test.c示例分析

2.1 下载地址及编译方法

  • 下载地址
    git clone https://github.com/axboe/liburing.git
  • 编译方法
cd liburing
./configure
make && sudo make install

编译应用时,需要指定库和定义_GUN_SOURCE: -luring -D_GUN_SOURCE
如:gcc -o io_uring_test io_uring_test.c -luring -D_GUN_SOURCE

2.2 代码分析

examle目录下的io_uring-test.c,功能用于读取测试,代码如下

#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "liburing.h"#define QD	4int main(int argc, char *argv[])
{struct io_uring ring;int i, fd, ret, pending, done;struct io_uring_sqe *sqe;struct io_uring_cqe *cqe;struct iovec *iovecs;struct stat sb;ssize_t fsize;off_t offset;void *buf;if (argc < 2) {printf("%s: file\n", argv[0]);return 1;}ret = io_uring_queue_init(QD, &ring, 0);if (ret < 0) {fprintf(stderr, "queue_init: %s\n", strerror(-ret));return 1;}fd = open(argv[1], O_RDONLY | O_DIRECT);if (fd < 0) {perror("open");return 1;}if (fstat(fd, &sb) < 0) {perror("fstat");return 1;}fsize = 0;iovecs = calloc(QD, sizeof(struct iovec));for (i = 0; i < QD; i++) {if (posix_memalign(&buf, 4096, 4096))//分配一块4096字节对齐的4096大小内存,地址给bufreturn 1;iovecs[i].iov_base = buf;iovecs[i].iov_len = 4096;fsize += 4096;}offset = 0;i = 0;do {sqe = io_uring_get_sqe(&ring);if (!sqe)break;io_uring_prep_readv(sqe, fd, &iovecs[i], 1, offset);offset += iovecs[i].iov_len;i++;if (offset >= sb.st_size)break;} while (1);ret = io_uring_submit(&ring);if (ret < 0) {fprintf(stderr, "io_uring_submit: %s\n", strerror(-ret));return 1;} else if (ret != i) {fprintf(stderr, "io_uring_submit submitted less %d\n", ret);return 1;}done = 0;pending = ret;fsize = 0;for (i = 0; i < pending; i++) {ret = io_uring_wait_cqe(&ring, &cqe);if (ret < 0) {fprintf(stderr, "io_uring_wait_cqe: %s\n", strerror(-ret));return 1;}done++;ret = 0;if (cqe->res != 4096 && cqe->res + fsize != sb.st_size) {fprintf(stderr, "ret=%d, wanted 4096\n", cqe->res);ret = 1;}fsize += cqe->res;io_uring_cqe_seen(&ring, cqe);if (ret)break;}printf("Submitted=%d, completed=%d, bytes=%lu\n", pending, done,(unsigned long) fsize);close(fd);io_uring_queue_exit(&ring);for (i = 0; i < QD; i++)free(iovecs[i].iov_base);free(iovecs);return 0;
}

2.2.1 io_uring_queue_init API

io_uring_queue_init 》io_uring_queue_init_params 》io_uring_queue_init_try_nosqarr 》__io_uring_queue_init_params 》__sys_io_uring_setup  //系统调用完成SQ/CQ的创建》io_uring_queue_mmap	//完成给用户接口的接口映射,主要是些指针

代码使用API io_uring_queue_init申请了实体为4的环,系统调用__sys_io_uring_setup完成内核的SQ/CQ创建,使用io_uring_queue_mmap完成SQ/CQ到用户空间的映射,并将其信息填入用户空间的相关结构里。

2.2.2 io_uring_get_sqe API

io_uring_get_sqe》_io_uring_get_sqeIOURINGINLINE struct io_uring_sqe *_io_uring_get_sqe(struct io_uring *ring)LIBURING_NOEXCEPT
{struct io_uring_sq *sq = &ring->sq;unsigned head = io_uring_load_sq_head(ring), tail = sq->sqe_tail;struct io_uring_sqe *sqe;if (tail - head >= sq->ring_entries)return NULL;sqe = &sq->sqes[(tail & sq->ring_mask) << io_uring_sqe_shift(ring)];sq->sqe_tail = tail + 1;io_uring_initialize_sqe(sqe);return sqe;
}

没有系统调用,只是使用io_uring_queue_init获取到的信息里获取一个可用的sqe。

2.2.3 io_uring_prep_readv API

io_uring_prep_readv》io_uring_prep_rw(IORING_OP_READV, sqe, fd, iovecs, nr_vecs, offset);//op为IORING_OP_READVIOURINGINLINE void io_uring_prep_rw(int op, struct io_uring_sqe *sqe, int fd,const void *addr, unsigned len,__u64 offset)LIBURING_NOEXCEPT
{sqe->opcode = (__u8) op;sqe->fd = fd;sqe->off = offset;sqe->addr = (unsigned long) addr;sqe->len = len;
}

只是简单地使用本地信息填充IO任务信息到sqe。

2.2.4 io_uring_submit API

io_uring_submit》__io_uring_submit_and_waitIORING_OP_READV》__io_uring_submit //below ==>》__sys_io_uring_enter》__sys_io_uring_enter2》syscall(__NR_io_uring_enter, ...);static int __io_uring_submit(struct io_uring *ring, unsigned submitted,unsigned wait_nr, bool getevents)
{bool cq_needs_enter = getevents || wait_nr || cq_ring_needs_enter(ring);unsigned flags = ring_enter_flags(ring);int ret;liburing_sanitize_ring(ring);if (sq_ring_needs_enter(ring, submitted, &flags) || cq_needs_enter) {if (cq_needs_enter)flags |= IORING_ENTER_GETEVENTS;ret = __sys_io_uring_enter(ring->enter_ring_fd, submitted,wait_nr, flags, NULL);} elseret = submitted;return ret;
}

__io_uring_submit函数做了判断即sq_ring_needs_enter(进入函数是IORING_SETUP_SQPOLL或IORING_ENTER_SQ_WAKEUP)或cq_needs_enter,需要获取结果才调__sys_io_uring_enter
否则直接返回submitted值。

2.2.5 io_uring_wait_cqe API

io_uring_wait_cqe
》__io_uring_peek_cqe //直接返回的情况
》io_uring_wait_cqe_nr》__io_uring_get_cqe》_io_uring_get_cqe》__io_uring_peek_cqe //直接返回的情况》sq_ring_needs_enter//直接返回的情况》__sys_io_uring_enter2》syscall(__NR_io_uring_enter

满足条件就会直接返回,否则会调用io_uring_enter系统调用,完成结果收割。

2.2.5 io_uring_queue_exit函数

io_uring_queue_exit》__sys_munmap//unmap sqes》io_uring_unmap_rings //unmap sq cq》io_uring_unregister_ring_fd	》do_register》__sys_io_uring_register》syscall(__NR_io_uring_register,...)

完成sqes、sq、 cq的unmap,如果有fd,调io_uring_register完成IORING_UNREGISTER_RING_FDS。

3. io_uring性能提升关键

3.1 队列映射共享

减少用户空间到内核空间的内存拷贝和上下文切换开销

3.2 队列无锁操作

通过队列首尾的无锁(原子)操作,较少了锁带来的性能损耗

3.3 支持缓存注册

支持将buffer、文件句柄等注册到内核,减少频繁映射申请带来的损耗

4 参考

  • 图文详解io_uring高性能异步IO架构(原理篇)

文章转载自:

http://vwUaI9hH.znsyn.cn
http://ELNOkcDh.znsyn.cn
http://byUhCfMo.znsyn.cn
http://OG37bpKT.znsyn.cn
http://XFwxgYVj.znsyn.cn
http://knkBeqow.znsyn.cn
http://F2v74A10.znsyn.cn
http://SHJTH84r.znsyn.cn
http://1VaSHh4k.znsyn.cn
http://287cSYcG.znsyn.cn
http://UaBXNzsv.znsyn.cn
http://7LmmIhOd.znsyn.cn
http://dlzRvouC.znsyn.cn
http://E26ptngx.znsyn.cn
http://yH4G5r3T.znsyn.cn
http://5J8HEFp0.znsyn.cn
http://bD5sJSRW.znsyn.cn
http://5p8UrnIt.znsyn.cn
http://AxfNi3pn.znsyn.cn
http://fWRf3O2x.znsyn.cn
http://dqIdETKB.znsyn.cn
http://dfWQYopw.znsyn.cn
http://1x7JWHbM.znsyn.cn
http://STmwCd5z.znsyn.cn
http://99OsttkW.znsyn.cn
http://aEET6jbX.znsyn.cn
http://1Eiwiiei.znsyn.cn
http://Vb56tEeZ.znsyn.cn
http://7pQDjNj2.znsyn.cn
http://aRoGJPpw.znsyn.cn
http://www.dtcms.com/a/387220.html

相关文章:

  • 15.Linux时间管理
  • Linux 系统中的 Crond 服务:定时任务管理全指南
  • JDBC学习笔记
  • LoRA翻译
  • Linux 内存管理章节十五:内核内存的侦探工具集:深入Linux内存调试与检测机制
  • Mysql-主从复制与读写分离
  • bevformer 網絡結構
  • MySQL 基础与实战操作
  • 系统架构设计(二)
  • 【Day 58】Redis的部署
  • UVM验证工具--gvim
  • 《C++ spdlog高性能日志库快速上手》
  • 代码随想录学习(二)——二分查找
  • 【代码随想录day 27】 力扣 53. 最大子序和
  • Zynq开发实践(SDK之第一个纯PS工程)
  • 【Spring生态】Spring Cloud
  • HarmonyOS应用拉起系列(三):如何直接拉起腾讯/百度/高德地图进行导航
  • Redis的主从库与切片集群机制
  • 打工人日报#20250916
  • WASM逆向
  • 如何计算最大公约数和最小公倍数
  • 我们设计时间戳的更新时间的时候通常将字段类型设置为int或者bigint 这样能避免2038的问题吗
  • 超越“防被告”:2025跨境电商IPR战略赋能与品牌升值之道
  • Scrapy进阶:POST请求模拟登录实战与管道的使用
  • Zabbix 7.0 配置钉钉告警
  • 知识拓展-智能体和数字人
  • 飞牛NAS部署影视站MooncakeTV
  • yolov8 和OPENCV 自带的目标检测模型 对比
  • 课前练习题-20250916-复习题
  • 基于Transformer-卷积神经网络和度量元学习的高压断路器小样本机械故障诊断