Linux·异步IO编程框架

2023-10-27

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 的出现

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux·异步IO编程框架 的相关文章

  • 如何在 Linux 中显示进程状态(阻塞、非阻塞)

    有没有办法查询 Linux 进程表中进程的状态 以便能够演示执行查询时进程是正在运行还是被阻止 我的目标是从进程或程序的 外部 执行此操作 因为我希望从操作系统进程的角度来理解这一点 但欢迎任何想法 这是Python代码阻塞的过程 impo
  • 如何修复 /usr/bin/env 参数处理?

    我遇到了一个奇怪的问题 usr bin env 我设计了一个简单的脚本来显示问题 该脚本使用 Ruby 编写 但使用 Python 编写的类似脚本也会发生同样的情况 这是脚本 usr bin env ruby p ARGV 还有一个没有 u
  • /usr/bin/as:无法识别的选项“-EL”

    因此 在为我的1plus手机编译android内核时 经过3天的多次尝试 我放弃了并尝试在这里询问是否有人以前遇到过这个问题 这个错误对我来说有点模糊 但我觉得问题来自于我最近对 GNU Linux 发行版 Gentoo 的更改 它在不应该
  • 尝试编译 git 但在 linux 中找不到 libcurl

    我想编译支持 http https 的 git 我有 ls usr include curl curlbuild h curl h curlrules h curlver h easy h mprintf h multi h stdchea
  • 检查 Linux 中给定进程的打开 FD 限制

    我最近有一个 Linux 进程 泄露 了文件描述符 它打开了文件描述符 但没有正确关闭其中一些文件描述符 如果我对此进行监控 我就可以提前得知该过程已达到其极限 有没有一种很好的 Bash 或 Python 方法来检查 Ubuntu Lin
  • Python 线程与 Linux 中的多处理

    基于此question https stackoverflow com questions 807506 threads vs processes in linux我假设创建新流程应该几乎和创造新线程在Linux中 然而 很少的测试显示出截
  • 查找当前打开的文件句柄数(不是 lsof )

    在 NIX系统上 有没有办法找出当前正在运行的进程中有多少个打开的文件句柄 我正在从正在运行的进程中寻找在 C 中使用的 API 或公式 在某些系统上 见下文 您可以在 proc pid fd 中对它们进行计数 如果不属于其中之一 请参阅下
  • Git - 致命:无法获取当前工作目录?

    When I git clone从回购协议中 我得到 fatal Could not get current working directory No such file or directory 我该怎么办 我检查了服务器并发现 git文
  • 带有接收器的 boost_log 示例无法编译

    我正在考虑将 boost log 用于一个项目 一开始我就遇到了以下问题 我在以下位置找到的升压日志示例 http www boost org doc libs 1 54 0 libs log example doc tutorial fi
  • 多线程进程的线程ID可以与另一个正在运行的进程的进程ID相同吗?

    我正在尝试找到一种方法来唯一标识多进程环境中的线程 我有一个服务器 它跟踪连接到它的不同进程 其中一些是多线程的 一些不是 为了识别多线程连接中的线程 我使用线程 ID 作为唯一标识符 在任何给定时间最多有 1 个多线程进程连接 我的问题是
  • Tomcat 中的 403 访问被拒绝

    我有以下内容tomcat users xml
  • pip 找不到满足要求的版本 django==2.2.1

    我刚刚将操作系统更改为 linux 并且想安装 django 但我无法安装最新版本的 django 我努力了 pip install django 但是它安装了 django 1 11 11 这不是我需要的 我还将我的 pip 升级到了 1
  • 串口读取未完成

    下面的函数用于在Linux下从串口读取数据 我在调试时可以读取完整的数据 但是当我启动程序时 读缓冲区似乎并不完整 我正确接收了一小部分数据 但缓冲区的其余部分完全正确zero 可能是什么问题呢 int8 t serial port ope
  • python 的 fcntl.flock 函数是否提供文件访问的线程级锁定?

    Python 的 fcnt 模块提供了一种名为 flock 1 的方法来证明文件锁定 其描述如下 对文件执行锁定操作op 描述符 fd 文件对象提供 fileno 方法被接受为 出色地 请参阅 Unix 手册集群 2 了解详情 在某些系统上
  • /proc/kmsg 和 dmsg 有什么区别?

    我们通常这样做cat proc kmsg or dmesg从用户空间查看内核日志 我明白了dmesg是一个循环缓冲区 它从kmsg 但是kmsg也不是循环缓冲区 它们之间有什么区别和联系呢 宽松地说 dmesg 是一个转储 proc kms
  • 64位版本的adb和fastboot?

    我在 Debian 7 3 x64 已完全修补 上发现了以下错误 我很确定这是因为adb即使在其 SDK 工具的 64 位发行版中也是 32 位 which adb opt android sdk platform tools adb op
  • C:运行系统命令并获取输出? [复制]

    这个问题在这里已经有答案了 我想在 Linux 中运行一个命令并获取其输出内容的返回文本 但我do not想要将此文本打印到屏幕上 有没有比制作临时文件更优雅的方法 你想要 popen http linux die net man 3 po
  • 主动\被动模式下 FTP 服务器的适当 iptables 规则

    我在 CentOS6 上安装了 ProFTPD 服务器 如果我使 ftp 本地主机 我可以正确连接 但如果我从外部尝试 我会收到消息 没有到主机的路由 但有一条到主机的路由 因为我是通过 SSH 连接的 我尝试添加以下 iptable 规则
  • Java 内存错误:无法创建新的本机线程

    运行 java 服务器时 我在 UNIX 服务器上收到此错误 Exception in thread Thread 0 java lang OutOfMemoryError unable to create new native threa
  • 为什么 ls -l 中的“总计”加起来不等于列出的总文件大小? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 为什么是total在输出中ls l打印为64并不是26078列出的所有文件的总数是多少 ls l test ls total 64 rw

随机推荐

  • Qt版本的选择与安装

    Qt简介 Qt是当下最流行的C 开发库 也是当下最流行的跨平台开发框架之一 可开发桌面端应用 移动端应用以及嵌入式端应用 可以说能支持的系统基本都支持 它可以做GUI但功能远不止GUI 我们用的最多的还是桌面端和嵌入式端的应用 移动端应用流
  • 最新Android Studio解决> No cached version of org.javailable for offline mode.

    问题 A problem occurred configuring root project kaikeba gt Could not resolve all artifacts for configuration classpath gt
  • 报错:Dependency annotations: {@org.springframework.beans.fact}

    我当时检查了下面的所有内容发现都没有错 最后在第五个监听器打错了 这给我气的 大家自己对照下面的内容仔细看看自己哪里错了吧 类检查方面 1 是否在加了 Controller Repository Service 注解 Controller
  • MySQL JDBC编程

    文章目录 什么是JDBC编程 JDBC的工作原理 JDBC的优点 JDBC的使用 插入操作 修改操作 删除操作 查询操作 什么是JDBC编程 JDBC编程就是用Java代码来操作数据库 JDBC即Java Database Connecti
  • STM32 - GPIO 详解

    GPIO 详解 文章目录 GPIO 详解 1 GPIO 是什么 2 STM32 引脚分类 3 GPIO 内部结构 3 1 保护二极管 3 2 上下拉电阻 3 3 P MOS 和 N MOS 3 4 输出数据寄存器 3 5 复用输出功能 3
  • vue2和vue3的区别

    原文地址 https www cnblogs com limou956259 p 17195546 html 1 双向数据绑定原理不同 vue2 vue2的双向数据绑定是利用ES5的一个API Object definePropert 对数
  • C++笔记

    C 中的cast 1 const cast 2 dynamic cast 3 static cast 4 reinterpret cast C 中variable的definition initialization assignment d
  • ‘git‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件

    需要确认自己已经下载了git 如果已经下载了git 就可能是git的路径发生的改变 更改环境变量配置即可 环境变量配置 1 进入电脑设置 关于 高级系统设置 2 点击环境变量 3 双击进入系统变量中的path 4 点击新建 5 根据自己的g
  • Maven解决jar包版本冲突的4种方法

    概念 先解释下maven的依赖传递 a jar包引入了b jar包 如果项目中引入了a jar包 其实也会把a依赖的b jar包引入 那现在有a c这2个jar包 a jar包依赖的是1 0 0版本的b jar包 c jar包也依赖了b j
  • 将图片保存成字符串,以及字符串转换为图片

    将图片保存成字符串的样子保存在excel表格 上代码 public class FileConfig 当前项目目录下的files public static String FILE ADDRESS PATH System getProper
  • C语言入门之工资计算

    include
  • Python 第一章 基础知识(3) 数字和表达式 加减乘除

    第一章 基础知识 3 数字和表达式 运行IDLE 在提示符前输入 加法 gt gt gt 2 2 4 lt 解释器会得出2 2的答案4 除法 gt gt gt 1 2 0 lt 解释器会给出截除掉小数部分的1除以2的商 gt gt gt 1
  • Mybatis-plus:条件查询的方法

    方法1 QueryWrapper
  • windows系统80端口号被System占用

    废话不多说 直入主题 windows系统80端口号被System占用 查找追踪看到是 PID 4 的一个System进程在占用 网上所说的解决方法 方法一 1 Win R 组合快捷键 快速打开运行命令框 在打开后面键入命令 Regedit
  • 码农得用专用的Code字体,推荐几款专业级别的程序员专用字体

    别怀疑 下面的这些字体是程序员专用的编码字体 尤其是带 Code 名字的字体 从名字上看就知道 专门用来Code用的 1 Source Code Pro PS那个公司知道吧 就是这个公司专门为程序员设计的等宽字体 要知道 写代码 一般的字体
  • 光束法空三的计算问题,误差方程的多余观测数,未知数个数、多余观测值的计算

    1 未知数个数 必要观测值个数 未知数个数 t u 3 未知点数目 6 相片数目 3 代表一个未知点的 x y z 6代表一张像片的6个外方位元素 都是待定值 都是未知数 2 观测值个数 1 未知数个数求法 1 观测值个数 n 2 m 的含
  • sparksql压缩小文件

    SET spark sql shuffle partitions 2 SET spark sql adaptive enabled true SET spark sql adaptive shuffle targetPostShuffleI
  • Kotlin 常见符号大全

    gt 下划线 as 1 修饰在成员变量的类型后面 表示这个变量可以为null 系统在任何情况下不会报它的空指针异常 修改在对象后面代表该对象如果为null则不处理后面逻辑 如果我们定义的变量是一个可以为空的类型 则要用 String 在变量
  • Qt、MinGW编译OpenCV 4.5.4(包含opencv_contrib)详细过程

    Qt MinGW CMake编译OpenCV 4 5 4 包含opencv contrib 详细过程 目录 Qt MinGW CMake编译OpenCV 4 5 4 包含opencv contrib 详细过程 1 工具下载准备 2 CMak
  • Linux·异步IO编程框架

    hi 大家好 今天分享一篇Linux异步IO编程框架文章 对比IO复用的epoll框架 到底性能提高多少 让我们看一看 译者序 本文组合翻译了以下两篇文章的干货部分 作为 io uring 相关的入门参考 How io uring and