liburing和Linux io_uring源码阅读

liburing
版本

系统内核版本: Linux 5.15.0-107-generic #117~20.04.1-Ubuntu x86_64 GNU/Linux

源代码版本: git@github.com:torvalds/linux.git v5.15

阅读入口

从最简单的代码看起,即阅读入口:examples/io_uring-test.c
此源文件内部调用liburing相关函数包含
io_uring_queue_initio_uring_get_sqeio_uring_prep_readvio_uring_submitio_uring_wait_cqeio_uring_cqe_seenio_uring_queue_exit
内核提供的函数仅有三个:io_uring_setup@425io_uring_enter@426io_uring_register@427
其中io_uring_queue_init调用的内核函数为io_uring_setup
以此为起点,阅读内核代码。

liburing调用栈
// 示例程序调用代码
struct io_uring_params p;
memset(&p, 0, sizeof(p));
p.flags = 0;
struct io_uring ring;
io_uring_queue_init_params(64, &ring, &p);
int io_uring_queue_init_params(unsigned entries, struct io_uring *ring,struct io_uring_params *p)
{int ret;ret = io_uring_queue_init_try_nosqarr(entries, ring, p, NULL, 0);return ret >= 0 ? 0 : ret;
}
static int io_uring_queue_init_try_nosqarr(unsigned entries, struct io_uring *ring,struct io_uring_params *p, void *buf,size_t buf_size)
{unsigned flags = p->flags;int ret;// 重点在__io_uring_queue_init_params函数p->flags |= IORING_SETUP_NO_SQARRAY;ret = __io_uring_queue_init_params(entries, ring, p, buf, buf_size);// 5.15内核不支持IORING_SETUP_NO_SQARRAY, 故会返回 -EINVAL// 所以真正执行的是下面的__io_uring_queue_init_paramsif (ret != -EINVAL || (flags & IORING_SETUP_NO_SQARRAY))return ret;p->flags = flags;return __io_uring_queue_init_params(entries, ring, p, buf, buf_size);
}
int __io_uring_queue_init_params(unsigned entries, struct io_uring *ring,struct io_uring_params *p, void *buf,size_t buf_size)
{int fd, ret = 0;unsigned *sq_array;unsigned sq_entries, index;memset(ring, 0, sizeof(*ring));// 省去一些无法走到的代码fd = __sys_io_uring_setup(entries, p);if (fd < 0) {return fd;}return fd
}

__sys_io_uring_setup进入内核代码

// 根据调用号425找到函数地址 io_uring_setup
// grep -rn "SYSCALL_DEFINE" $(find ./ -name '*.c')
static long io_uring_setup(u32 entries, struct io_uring_params __user *params)
{struct io_uring_params p;int i;// 将数据从用户空间拷贝到内核空间if (copy_from_user(&p, params, sizeof(p)))return -EFAULT;for (i = 0; i < ARRAY_SIZE(p.resv); i++) {if (p.resv[i])return -EINVAL;}if (p.flags & ~(IORING_SETUP_IOPOLL | IORING_SETUP_SQPOLL |IORING_SETUP_SQ_AFF | IORING_SETUP_CQSIZE |IORING_SETUP_CLAMP | IORING_SETUP_ATTACH_WQ |IORING_SETUP_R_DISABLED))return -EINVAL;return io_uring_create(entries, &p, params);
}

由于io_uring_create篇幅过长,走不到的地方将会对其进行裁剪

static int io_uring_create(unsigned entries, struct io_uring_params *p,struct io_uring_params *params)
{// p为内核空间地址数据// params为用户空间地址数据struct io_ring_ctx *ctx;struct file *file;int ret;// 此处检查提交队列SQ大小是否会超过最大限制IORING_MAX_ENTRIES(0x8000)if (entries > IORING_MAX_ENTRIES) {if (!(p->flags & IORING_SETUP_CLAMP))return -EINVAL;entries = IORING_MAX_ENTRIES;}// 保证队列容量是2的幂次方p->sq_entries = roundup_pow_of_two(entries);// 完成队列CQ是提交队列容量的两倍p->cq_entries = 2 * p->sq_entries;// 初始化io_ring_ctx对象ctx = io_ring_ctx_alloc(p);if (!ctx)return -ENOMEM;ctx->compat = in_compat_syscall();if (!capable(CAP_IPC_LOCK))ctx->user = get_uid(current_user());mmgrab(current->mm);ctx->mm_account = current->mm;// 申请ctx->rings和ctx->sq_sqes内存ret = io_allocate_scq_urings(ctx, p);if (ret)goto err;// 设置了轮询后创建轮询线程函数, 由于参数未设置, 不在展开ret = io_sq_offload_create(ctx, p);if (ret)goto err;ret = io_rsrc_node_switch_start(ctx);if (ret)goto err;io_rsrc_node_switch(ctx, NULL);// 保存结构体成员偏移地址// 用于用户空间创建内存映射时获取到偏移地址, 将用户空间结构体内部变量和内核空间结构体内部变量一一对应// liburing io_uring_setup_ring_pointersmemset(&p->sq_off, 0, sizeof(p->sq_off));/*** 由于代码中设置了按照cacheline字节对齐* 所以p->sq_off.tail = 64, 即虽然head是4字节, 但为了防止`错误共享`, 让其占据一个cacheline大小* cacheline一般为64, 也可能是128, 博主PC是64的* 错误共享知识可查看* https://github.com/TonyBeen/study/blob/master/false_sharing/false_sharing.cc* * struct io_uring {*     u32 head __attribute__((__aligned__(64)));*     u32 tail __attribute__((__aligned__(64)));* };*/p->sq_off.head = offsetof(struct io_rings, sq.head);                    // 0p->sq_off.tail = offsetof(struct io_rings, sq.tail);                    // 64p->sq_off.ring_mask = offsetof(struct io_rings, sq_ring_mask);          // 256p->sq_off.ring_entries = offsetof(struct io_rings, sq_ring_entries);    // 264p->sq_off.flags = offsetof(struct io_rings, sq_flags);                  // 276p->sq_off.dropped = offsetof(struct io_rings, sq_dropped);              // 272/*** 具体细节在函数io_allocate_scq_urings内部, 如下* 下面介绍如何计算得到2368的(代码在rings_size中)** sizeof(struct io_rings) = 320 (64字节对齐结果)* sizeof(struct io_uring_cqe) = 16* cq_entries = 128* * p->sq_off.array = cq_entries * sizeof(struct io_uring_cqe) + sizeof(struct io_rings)* 得到结果满足在cacheline字节边界上, 不满足的情况下不会是2368* */p->sq_off.array = (char *)ctx->sq_array - (char *)ctx->rings;           // 2368memset(&p->cq_off, 0, sizeof(p->cq_off));p->cq_off.head = offsetof(struct io_rings, cq.head);p->cq_off.tail = offsetof(struct io_rings, cq.tail);p->cq_off.ring_mask = offsetof(struct io_rings, cq_ring_mask);p->cq_off.ring_entries = offsetof(struct io_rings, cq_ring_entries);p->cq_off.overflow = offsetof(struct io_rings, cq_overflow);p->cq_off.cqes = offsetof(struct io_rings, cqes);p->cq_off.flags = offsetof(struct io_rings, cq_flags);p->features = IORING_FEAT_SINGLE_MMAP | IORING_FEAT_NODROP |IORING_FEAT_SUBMIT_STABLE | IORING_FEAT_RW_CUR_POS |IORING_FEAT_CUR_PERSONALITY | IORING_FEAT_FAST_POLL |IORING_FEAT_POLL_32BITS | IORING_FEAT_SQPOLL_NONFIXED |IORING_FEAT_EXT_ARG | IORING_FEAT_NATIVE_WORKERS |IORING_FEAT_RSRC_TAGS;// 拷贝到用户空间if (copy_to_user(params, p, sizeof(*p))) {ret = -EFAULT;goto err;}// 以下是创建一个匿名 inode, 并将文件描述符返回// 可通过ll /proc/{pid}/fd/ 查看到io_uring的文件描述 3 -> 'anon_inode:[io_uring]'// 用户可通过返回文件描述符进行mmap, 以获取对SQ/CQ的内存访问file = io_uring_get_file(ctx);if (IS_ERR(file)) {ret = PTR_ERR(file);goto err;}/** Install ring fd as the very last thing, so we don't risk someone* having closed it before we finish setup*/ret = io_uring_install_fd(ctx, file);if (ret < 0) {/* fput will clean it up */fput(file);return ret;}trace_io_uring_create(ret, ctx, p->sq_entries, p->cq_entries, p->flags);return ret;
err:io_ring_ctx_wait_and_kill(ctx);return ret;
}
static int io_allocate_scq_urings(struct io_ring_ctx *ctx,struct io_uring_params *p)
{struct io_rings *rings;size_t size, sq_array_offset;/* make sure these are sane, as we already accounted them */ctx->sq_entries = p->sq_entries;ctx->cq_entries = p->cq_entries;// rings_size计算的是一块存储struct io_rings + struct io_uring_cqe + sq_array的连续内存// sq_array是ctx->sq_sqes的索引数组, 内存大小 = p->sq_entries * sizeof(u32) = 256// sq_array_offset = 2368// size = 2368 + 256 = 2624size = rings_size(p->sq_entries, p->cq_entries, &sq_array_offset);if (size == SIZE_MAX)return -EOVERFLOW;rings = io_mem_alloc(size);if (!rings)return -ENOMEM;ctx->rings = rings;ctx->sq_array = (u32 *)((char *)rings + sq_array_offset);rings->sq_ring_mask = p->sq_entries - 1;rings->cq_ring_mask = p->cq_entries - 1;rings->sq_ring_entries = p->sq_entries;rings->cq_ring_entries = p->cq_entries;size = array_size(sizeof(struct io_uring_sqe), p->sq_entries);if (size == SIZE_MAX) {io_mem_free(ctx->rings);ctx->rings = NULL;return -EOVERFLOW;}ctx->sq_sqes = io_mem_alloc(size);if (!ctx->sq_sqes) {io_mem_free(ctx->rings);ctx->rings = NULL;return -ENOMEM;}return 0;
}

返回用户空间

int __io_uring_queue_init_params(unsigned entries, struct io_uring *ring,struct io_uring_params *p, void *buf,size_t buf_size)
{int fd, ret = 0;unsigned *sq_array;unsigned sq_entries, index;memset(ring, 0, sizeof(*ring));// 省去一些无法走到的代码fd = __sys_io_uring_setup(entries, p);if (fd < 0) {return fd;}// ------> 从此处执行 <------// 未设置IORING_SETUP_NO_MMAP(不使用内存映射)标志if (!(p->flags & IORING_SETUP_NO_MMAP)) {// io_uring_queue_mmap透传, 直接看io_uring_mmapret = io_uring_queue_mmap(fd, p, ring);if (ret) {__sys_close(fd);return ret;}}return ret;
}
static int io_uring_mmap(int fd, struct io_uring_params *p,struct io_uring_sq *sq, struct io_uring_cq *cq)
{size_t size;int ret;size = sizeof(struct io_uring_cqe); // 16// 通过gdb查看/*** p->sq_off.array = 2368* p->sq_entries = 64* * p->cq_off.cqes = 320 // 固定320* p->cq_entries = 128* * 计算出结果* sq->ring_sz = 2624* cq->ring_sz = 2368*/sq->ring_sz = p->sq_off.array + p->sq_entries * sizeof(unsigned);cq->ring_sz = p->cq_off.cqes + p->cq_entries * size;// fs/io_uring.c line:10328 会设置IORING_FEAT_SINGLE_MMAP标志if (p->features & IORING_FEAT_SINGLE_MMAP) {if (cq->ring_sz > sq->ring_sz)sq->ring_sz = cq->ring_sz;cq->ring_sz = sq->ring_sz;}/*** sq->ring_sz = 2624* cq->ring_sz = 2624*/// 创建与内核结构体io_rings的内存映射sq->ring_ptr = __sys_mmap(0, sq->ring_sz, PROT_READ | PROT_WRITE,MAP_SHARED | MAP_POPULATE, fd,IORING_OFF_SQ_RING);if (IS_ERR(sq->ring_ptr))return PTR_ERR(sq->ring_ptr);// 共用一份数据if (p->features & IORING_FEAT_SINGLE_MMAP) {cq->ring_ptr = sq->ring_ptr;}size = sizeof(struct io_uring_sqe); // 64// 创建提交队列的内存映射// 此结构体数据用于给用户填充数据, 如通过io_uring_get_sqe获取一个结构体// 并通过io_uring_prep_read填充此结构体, 最后通过io_uring_submit提交// NOTE 需要注意的是, 通过io_uring_prep_read设置的数据必须保证内存生命周期在完成后sq->sqes = __sys_mmap(0, size * p->sq_entries, PROT_READ | PROT_WRITE,MAP_SHARED | MAP_POPULATE, fd, IORING_OFF_SQES);// 初始化提交队列和完成队列io_uring_setup_ring_pointers(p, sq, cq);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/35541.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

富唯智能推出的AMR复合机器人铝板CNC上下料方案

随着科技的不断进步&#xff0c;CNC加工行业正面临着前所未有的变革。传统的CNC上下料方式已无法满足现代生产对效率、精度和安全性的高要求。在这样的背景下&#xff0c;富唯智能推出的AMR复合机器人铝板CNC上下料方案&#xff0c;以其智能化、自动化的特点&#xff0c;引领了…

Mind+在线图形编程软件(Sractch类软件)

Scratch作为图形编程软件&#xff0c;可以为小朋友学习编程提供很好的入门&#xff0c;是初次接触编程的小朋友的首选开发软件。这里介绍的Mind软件与Sractch用法几乎完全一致&#xff0c;并且可以提供在线免安装版本使用&#xff0c;浏览器直接打开网址&#xff1a; ide.mindp…

LeetCode.32最长有效括号详解

问题描述 给你一个只包含 ( 和 ) 的字符串&#xff0c;找出最长有效&#xff08;格式正确且连续&#xff09;括号子串的长度。 解题思路1 有效的括号字符串意味着每一个左括号 ( 都可以找到一个相匹配的右括号 )。栈可以帮助我们追踪尚未匹配的括号&#xff0c;并有效地处理…

Micro-ROS是什么?

Micro-ROS是ROS&#xff08;Robot Operating System&#xff0c;机器人操作系统&#xff09;生态系统的一个重要组成部分&#xff0c;专为微控制器&#xff08;Microcontrollers&#xff09;设计的轻量级ROS版本。它的目标是在资源有限的嵌入式平台上实现ROS 2的功能&#xff0…

RocketMQ:日常开发中有哪些使用MQ的场景

什么是消息队列&#xff1f; 消息队列是一种通信方法&#xff0c;允许应用程序通过发送和接收消息来互相通信。这些消息/任务/指令存储在一个中间介质中&#xff08;即队列&#xff09;&#xff0c;并由生产者发送&#xff0c;消费者接收。 使用场景 场景一&#xff1a;任务…

对于使用 C 语言开发的跨平台应用,如何解决不同操作系统和硬件架构带来的底层差异和兼容性问题?

在使用C语言开发跨平台应用时&#xff0c;可以采取以下方法来解决不同操作系统和硬件架构带来的底层差异和兼容性问题&#xff1a; 使用平台无关的标准库&#xff1a;使用C语言标准库提供的函数和数据类型&#xff0c;避免直接使用操作系统特定的函数和数据类型。 使用条件编译…

昇思25天学习打卡营第1天|基本介绍

MindSpore 基本介绍设计理念层次结构 基本介绍 昇思MindSpore是一个全场景深度学习框架&#xff0c;旨在实现易开发、高效执行、全场景统一部署三大目标。 易开发&#xff1a;API友好、调试难度低高效执行&#xff1a;包括计算效率、数据预处理效率和分布式训练效率全场景&am…

手写SpringMVC之ApplicationContextListener

什么是Spring MVC&#xff1f; Spring Web MVC是基于Servlet API构建的原始Web框架&#xff0c;从一开始就包含在Spring Framework中。正式名称“Spring Web MVC”来自其源模块的名称&#xff08; spring-webmvc &#xff09;&#xff0c;但它通常被称为“Spring MVC”。 手写…

hive-LEAD() over() 取字段的下一个值

lead(status,1,null) over(partition by shop oreder by month) as next_status --lead()参数1&#xff1a;目标字段;参数2&#xff1a;步长&#xff08;是取下1个还是下2个&#xff09;;参数3&#xff1a;取不到给NULL SELECTa.related_org_code,a.camera_id,a.event_ti…

【elasticsearch】es6重启服务后数据消失,es6如何配置数据持久化储存

服务器重启后之前添加进去的数据消失了,排查了一圈发现数据没有进行持久化保存导致的 在Elasticsearch 6.0.0中,数据的持久化存储主要通过以下几个配置来实现: 1、path.data: 指定Elasticsearch用于存储数据的目录。 2、path.logs: 指定Elasticsearch日志文件存储的目录。 …

【python】OpenCV—Color Map

文章目录 cv2.applyColorMapcv2.putText小试牛刀自定义颜色 参考学习来自 OpenCV基础&#xff08;21&#xff09;使用 OpenCV 中的applyColorMap实现伪着色 cv2.applyColorMap cv2.applyColorMap() 是 OpenCV 中的一个函数&#xff0c;用于将灰度图像或单通道图像应用一个颜色…

The First Descendant卡顿怎么办?快速处理第一后裔卡顿

第一后裔/The First Descendant是一款刷宝类RPG射击游戏&#xff0c;以虚幻引擎5为基础&#xff0c;使其对决场景十分精彩、刺激&#xff0c;从而吸引了大批冒险者前往&#xff0c;这里揭开属于英格里斯和继承者的秘密。不过有很多玩家&#xff0c;反馈在对局时遇到了卡顿、画面…

Ubuntu 20.04.3 LTS 安装打印服务器 局域网发现系统服务 共享给 windows 10/11 使用

ubuntu安装部署打印服务器可参考: Ubuntu 20.04.3 LTS 安装hp 1020 plus 打印机 通过cups共享给 windows 10/11 使用-CSDN博客 1 windows 10 ,局域网搜索不到共享的hp1020打印机 2 Ubuntu使用Avahi进行局域网服务发现和设备发现&#xff0c;安装avahi-daemon sudo apt-updat…

计算机的核心、线程、进程,任务、指令,他们之间的关系及工作原理

一、基本概念 1&#xff09;指令的含义及组成 定义&#xff1a;指令是计算机程序发给处理器的命令&#xff0c;它是计算机硬件语言系统&#xff08;机器语言&#xff09;的一部分&#xff0c;用来指挥CPU执行特定的操作。内容&#xff1a;一条指令通常包括操作码和地址码。操…

【Linux】Linux下使用套接字进行网络编程

&#x1f525;博客主页&#xff1a; 我要成为C领域大神&#x1f3a5;系列专栏&#xff1a;【C核心编程】 【计算机网络】 【Linux编程】 【操作系统】 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 本博客致力于知识分享&#xff0c;与更多的人进行学习交流 ​ 用于网络应用开…

DNS自动择优:提升网络体验的新途径

随着互联网的深入发展和广泛应用&#xff0c;网络速度和网络稳定性成为了用户关注的重点。在这个过程中&#xff0c;DNS&#xff08;Domain Name System&#xff0c;域名系统&#xff09;的作用不容忽视。近年来&#xff0c;DNS自动择优技术的出现&#xff0c;为提升网络体验带…

Java基础(四)——字符串、StringBuffer、StringBuilder、StringJoiner

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 ⚡开源项目&#xff1a; rich-vue3 &#xff08;基于 Vue3 TS Pinia Element Plus Spring全家桶 MySQL&#xff09; &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1…

git pull 参与别人的项目

简述 git pull主要是实现本地项目的上推。主要用在将本地更改提交到 GitHub 上别人的项目&#xff08;即在线原始项目&#xff09;。以下是基本操作逻辑&#xff1a; Fork 原始项目&#xff1a;在 GitHub 上 fork 原始项目到个人账户。克隆 Fork 的项目&#xff1a;将 fork 的…

磁力链接搜索大全教程,如何使用磁力链接。

磁力链接是一种特殊的下载链接&#xff0c;磁力链接可以理解为一个文件识别码&#xff0c;而并非具体的资源地址&#xff0c;下载软件需要拿着这个识别码去整个互联网(DHT网络)去寻找持有该资源的用户(节点)&#xff0c;如果找到则可以进行传输下载。一般年代越久远的磁力链接下…

6、广告-RTB竞价逻辑

在程序化广告中&#xff0c;技术的应用至关重要&#xff0c;尤其是RTB&#xff08;实时竞价&#xff09;的竞价逻辑。以下详细介绍RTB竞价逻辑&#xff0c;并提供相关的中文名词与英文名词对应。 一、RTB竞价逻辑&#xff08;Real-Time Bidding Logic&#xff09; RTB是程序化…