Linux·异步IO编程框架

hi,大家好,今天分享一篇Linux异步IO编程框架文章,对比IO复用的epoll框架,到底性能提高多少?让我们看一看。

译者序

本文组合翻译了以下两篇文章的干货部分,作为 io_uring 相关的入门参考:

  • How io_uring and eBPF Will Revolutionize Programming in Linux[1], ScyllaDB, 2020
  • An Introduction to the io_uring Asynchronous I/O Framework[2], Oracle, 2020

io_uring 是 2019 年 Linux 5.1 内核首次引入的高性能异步 I/O 框架,能显着加速 I/O 密集型应用的性能。但如果你的应用 已经在使用 传统 Linux AIO 了, 并且使用方式恰当, 那 io_uring 并不会带来太大的性能提升 —— 根据原文测试(以及我们 自己的复现),即便打开高级特性,也只有 5%。除非你真的需要这 5% 的额外性能,否则切换成 io_uring 代价可能也挺大,因为要重写应用来适配 io_uring(或者让依赖的平台或框架去适配,总之需要改代码)。

既然性能跟传统 AIO 差不多,那为什么还称 io_uring 为革命性技术呢?

  1. 它首先和最大的贡献在于:统一了 Linux 异步 I/O 框架
  • Linux AIO 只支持 direct I/O 模式的 存储文件(storage file),而且主要用在 数据库这一细分领域
  • io_uring 支持存储文件和网络文件(network sockets),也支持更多的异步系统调用 (accept/openat/stat/...),而非仅限于 read/write 系统调用。
  • 在 设计上是真正的异步 I/O,作为对比,Linux AIO 虽然也 是异步的,但仍然可能会阻塞,某些情况下的行为也无法预测;
    似乎之前 Windows 在这块反而是领先的,更多参考:
  • 浅析开源项目之 io_uring[3],“分步试存储”专栏,知乎
  • Is there really no asynchronous block I/O on Linux?[4],stackoverflow
  • 灵活性和可扩展性非常好,甚至能基于 io_uring 重写所有系统调用,而 Linux AIO 设计时就没考虑扩展性。

eBPF 也算是异步框架(事件驱动),但与 io_uring 没有本质联系,二者属于不同子系统, 并且在模型上有一个本质区别:

  1. eBPF 对用户是透明的,只需升级内核(到合适的版本), 应用程序无需任何改造
  2. io_uring 提供了 新的系统调用和用户空间 API,因此 需要应用程序做改造

eBPF 作为动态跟踪工具,能够更方便地排查和观测 io_uring 等模块在执行层面的具体问题。

本文介绍 Linux 异步 I/O 的发展历史,io_uring 的原理和功能, 并给出了一些程序示例和性能压测结果(我们在 5.10 内核做了类似测试,结论与原文差不多)。

由于译者水平有限,本文不免存在遗漏或错误之处。如有疑问,请查阅原文。

以下是译文。


很多人可能还没意识到,Linux 内核在过去几年已经发生了一场革命。这场革命源于两个激动人心的新接口的引入:eBPF 和 io_uring。我们认为,二者将会完全 改变应用与内核交互的方式,以及应用开发者思考和看待内核的方式

本文介绍 io_uring(我们在 ScyllaDB 中有 io_uring 的深入使用经验),并略微提及一下 eBPF。

1 Linux I/O 系统调用演进

1.1 基于 fd 的阻塞式 I/O:read()/write()

作为大家最熟悉的读写方式,Linux 内核提供了 基于文件描述符的系统调用, 这些描述符指向的可能是 存储文件(storage file),也可能是 network sockets

ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);

二者称为 阻塞式系统调用(blocking system calls),因为程序调用 这些函数时会进入 sleep 状态,然后被调度出去(让出处理器),直到 I/O 操作完成:

  • 如果数据在文件中,并且文件内容 已经缓存在 page cache 中,调用会 立即返回
  • 如果数据在另一台机器上,就需要通过网络(例如 TCP)获取,会阻塞一段时间;
  • 如果数据在硬盘上,也会阻塞一段时间。

但很容易想到,随着存储 设备越来越快,程序越来越复杂, 阻塞式(blocking)已经这种最简单的方式已经不适用了。

1.2 非阻塞式 I/O:select()/poll()/epoll()

阻塞式之后,出现了一些新的、非阻塞的系统调用,例如 select()poll() 以及更新的 epoll()。应用程序在调用这些函数读写时不会阻塞,而是 立即返回,返回的是一个已经 ready 的文件描述符列表

但这种方式存在一个致命缺点:只支持 network sockets 和 pipes ——epoll() 甚至连 storage files 都不支持。

1.3 线程池方式

对于 storage I/O,经典的解决思路是 thread pool[5]:主线程将 I/O 分发给 worker 线程,后者代替主线程进行阻塞式读写,主线程不会阻塞。

这种方式的问题是 线程上下文切换开销可能非常大,后面性能压测会看到。

1.4 Direct I/O(数据库软件):绕过 page cache

随后出现了更加灵活和强大的方式:数据库软件(database software) 有时 并不想使用操作系统的 page cache[6], 而是希望打开一个文件后, 直接从设备读写这个文件(direct access to the device)。这种方式称为 直接访问(direct access)或 直接 I/O(direct I/O),

  • 需要指定 O_DIRECT flag;
  • 需要 应用自己管理自己的缓存 —— 这正是数据库软件所希望的;
  • 是 zero-copy I/O,因为应用的缓冲数据直接发送到设备,或者直接从设备读取。

1.5 异步 IO(AIO)

前面提到,随着存储设备越来越快,主线程和 worker 线性之间的上下文切换开销占比越来越高。现在市场上的一些设备,例如 Intel Optane[7], 延迟已经低到和上下文切换一个量级(微秒 us)。换个方式描述, 更能让我们感受到这种开销:上下文每切换一次,我们就少一次 dispatch I/O 的机会

因此,Linux 2.6 内核引入了异步 I/O(asynchronous I/O)接口, 方便起见,本文简写为 linux-aio。AIO **原理**是很简单的:

  • 用户通过 io_submit() 提交 I/O 请求,
  • 过一会再调用 io_getevents() 来检查哪些 events 已经 ready 了。
  • 使程序员 能编写完全异步的代码

近期,Linux AIO 甚至支持了[8] epoll():也就是说 不仅能提交 storage I/O 请求,还能提交网络 I/O 请求。照这样发展下去,linux-aio似乎能成为一个王者。但由于它糟糕的演进之路,这个愿望几乎不可能实现了。我们从 Linus 标志性的激烈言辞中就能略窥一斑

Reply to:  to support opening files asynchronously[9]
So I think this is ridiculously ugly.
AIO is a horrible ad-hoc design, with the main excuse being “other, less gifted people, made that design, and we are implementing it for compatibility because database people — who seldom have any shred of taste — actually use it”.
— Linus Torvalds (on  http://lwn.net)

首先,作为数据库从业人员,我们想借此机会为我们的没品(lack of taste)向 Linus 道歉。但更重要的是,我们要进一步解释一下 为什么 Linus 是对的:Linux AIO 确实问题缠身,

  1. 只支持O_DIRECT文件,因此 对常规的非数据库应用(normal, non-database applications) 几乎是无用的
  2. 接口在 设计时并未考虑扩展性。虽然可以扩展 —— 我们也确实这么做了 —— 但每加一个东西都相当复杂;
  3. 虽然从 技术上说接口是非阻塞的,但实际上有很多可能的原因都会导致它阻塞[10],而且引发的方式难以预料。

1.6 小结

以上可以清晰地看出 Linux I/O 的演进:

  • 最开始是同步(阻塞式)系统调用;
  • 然后随着 实际需求和具体场景,不断加入新的异步接口,还要保持与老接口的兼容和协同工作。

另外也看到,在非阻塞式读写的问题上 并没有形成统一方案

  1. Network socket 领域:添加一个异步接口,然后去轮询(poll)请求是否完成(readiness);
  2. Storage I/O 领域:只针对某一细分领域(数据库)在某一特定时期的需求,添加了一个定制版的异步接口。

这就是 Linux I/O 的演进历史 —— 只着眼当前,出现一个问题就引入一种设计,而并没有多少前瞻性 —— 直到 io_uring 的出现

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【RocketMQ】顺序消息实现原理

全局有序 在RocketMQ中,如果使消息全局有序,可以为Topic设置一个消息队列,使用一个生产者单线程发送数据,消费者端也使用单线程进行消费,从而保证消息的全局有序,但是这种方式效率低,一般不使用…

Web 攻防之业务安全:接口未授权访问/调用测试(敏感信息泄露)

Web 攻防之业务安全:接口未授权访问/调用测试 业务安全是指保护业务系统免受安全威胁的措施或手段。广义的业务安全应包括业务运行的软硬件平台(操作系统、数据库,中间件等)、业务系统自身(软件或设备)、业…

ViT/vit/VIT详解

参考: Vision Transformer详解: https://blog.csdn.net/qq_37541097/article/details/118242600 目录: x.1 (论文中)模型理解x.2 代码理解 建议阅读时间:10min x.1 模型理解 ViT是发表在ICLR2021上的一篇文章,通过将图片分割…

Java并发控制 学习笔记1

一、并发控制的方法 1、悲观锁:常用的互斥锁都属于悲观锁,一个线程访问共享资源时其他线程不能访问。 2、乐观锁:允许同时访问共享数据,只有在提交时利用如版本号检查是否有冲突,应用github。 3、什么时候用乐观锁、什…

携程平台增长部总经理王绩强:原生互联网企业正在经历一场数字升级丨数据猿专访...

‍数据智能产业创新服务媒体——聚焦数智 改变商业以大数据和人工智能为核心,众多新兴技术开始赋能数字营销。于是,智能营销已然从工具化走向了业务化。如今,数字化营销已经成为了企业数字化转型中的重要一环。相较于传统营销逻辑&#xff0…

新版新款影视直播粉红色UI的麻豆CMS源码/带教程/支付已接

基于苹果CMS v10影视系统框架开发的前端模板,带会员中心,可设置试看付费观看等功能。 经过测试及修复,这套源码功能还是很强大的,可以设置一键采集,并且支付我们给他接到了易支付,拓展性强,基本…

【压测】通过Jemeter进行压力测试(超详细)

文章目录背景一、前言二、关于JMeter三、准备工作四、创建测试4.1、创建线程组4.2、配置元件4.3、构造HTTP请求4.4、添加HTTP请求头4.5、添加断言4.6、添加察看结果树4.7、添加Summary Report4.8、测试计划创建完成五、执行测试计划总结背景 通过SpringCloudGateway整合Nacos进…

如何下载ChatGPT-ChatGPT如何写作

CHATGPT能否改一下文章 ChatGPT 作为一种自然语言处理技术,生成的文章可能存在表达不够准确或文风不符合要求等问题。在这种情况下,可以使用编辑和修改来改变输出的文章,使其符合特定的要求和期望。 具体来说,可以采用以下步骤对…

超越竞争对手:利用Facebook A/B测试优化广告效果!

随着社交媒体广告的普及,Facebook已经成为了许多公司推广业务的重要平台。但是,在Facebook上发布广告并不意味着成功,这也让许多公司开始关注如何优化广告效果。 在这篇文章中,我将介绍如何使用A/B测试来优化Facebook广告&#x…

纳米软件关于集成电路测试的分类介绍

集成电路测试可以按照测试目的、测试内容、按照器件开发和制造阶段分类。参照需要达到的测试目的对集成电路测试进行分类,可以分为:验证测试、制造测试、老化测试、入厂测试等。按照测试所涉及内容,集成电路测试可分为:参数测试、功能测试、结构测试等。…

2023/4/4总结

题解: Problem ​​​​​​ A - Codeforces 1.这道题目我们需要判断。 2.如果是奇数,亦或出来的总值不为0,那么每一个数字再去亦或任何一个数字,都不会为0。 3.如果是偶数并且亦或总值为0,那么我们亦或的总值不满…

记录重启csdn

有太多收藏的链接落灰了,在此重启~ 1、社会 https://mp.weixin.qq.com/s/Uq0koAbMUk8OFZg2nCg_fg https://mp.weixin.qq.com/s/yCtLdEWSKVVAKhvLHxjeig https://zhuanlan.zhihu.com/p/569162335?utm_mediumsocial&utm_oi938179755602853888&ut…

使用npm包,全局共享数据,分包

使用 npm 包 1、Vant Weapp 1.1、什么是 Vant Weapp Vant Weapp 是有赞前端团队开源的一套小程序 UI 组件库,助力开发者快速搭建小程序应用。它所使用的是MIT 开源许可协议,对商业使用比较友好。 官方文档地址 https://youzan.github.io/vant-weapp …

Huggingface微调BART的代码示例:WMT16数据集训练新的标记进行翻译

BART模型是用来预训练seq-to-seq模型的降噪自动编码器(autoencoder)。它是一个序列到序列的模型,具有对损坏文本的双向编码器和一个从左到右的自回归解码器,所以它可以完美的执行翻译任务。 如果你想在翻译任务上测试一个新的体系…

游戏运营专员的职责有哪些?提高游戏收入的关键是什么?

游戏运营是将一款游戏平台推入市场,通过对平台的运作,使用户从接触、认识、再到了解实际线上的一种操作、最终成为这款游戏平台的忠实玩家的这一过程。同时通过一系列的营销手段达到提高线上人数,刺激消费增长利润的目的。 游戏运营专员的职…

Go 连接池的设计与实现

为什么需要连接池 如果不用连接池,而是每次请求都创建一个连接是比较昂贵的,因此需要完成3次tcp握手 同时在高并发场景下,由于没有连接池的最大连接数限制,可以创建无数个连接,耗尽文件描述符 连接池就是为了复用这…

高效的实现金蝶云星空ERP与自研MES系统数据集成

一、项目背景 随着企业数字化转型的不断深入,数据集成变得愈发重要。金蝶云星空ERP与自研MES系统之间的数据集成是企业提高管理效率、降低运营成本的关键。为了实现这一目标,企业选择了轻易云数据集成平台进行数据集成。 二、项目实施过程 低耦合、高内…

二叉树的前序遍历(力扣144)

目录 题目描述: 解法一:递归法 解法二:迭代法 解法三:Morris 遍历 二叉树的前序遍历 题目描述: 给你二叉树的根节点 root ,返回它节点值的 前序 遍历。 示例 1: 输入:root […

Unity反编译:AssetStudio资源浏览器及代码查看器

前言 假如你手上有Unity发布出来的exe文件、apk文件或者webGL文件,但就是没有工程源文件,那么,如何从这些文件里面一窥究竟呢?这就需要资源提取工具以及代码反编译工具! 本文所涉软件【文中附有下载链接】&#xff1…

【接口测试工具】Eolink Apikit 快速入门教程

Eolink Apikit 下载安装【官方版】:https://www.eolink.com/apikit 发起 API 测试 进入 API 文档详情页,点击上方 测试 标签,进入 API 测试页,系统会根据 API 文档自动生成测试界面并且填充测试数据。 填写请求参数 首先填写好请…