毕业论文
您现在的位置: 框架 >> 框架优势 >> 正文 >> 正文

Linux异步IO框架iour

来源:框架 时间:2022/5/27

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

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

1、它首先和最大的贡献在于:统一了Linux异步I/O框架,

LinuxAIO只支持directI/O模式的存储文件(storagefile),而且主要用在数据库这一细分领域;

io_uring支持存储文件和网络文件(networksockets),也支持更多的异步系统调用(accept/openat/stat/...),而非仅限于read/write系统调用。

2、在设计上是真正的异步I/O,作为对比,LinuxAIO虽然也是异步的,但仍然可能会阻塞,某些情况下的行为也无法预测;

3、**灵活性和可扩展性**非常好,甚至能基于io_uring重写所有系统调用,而LinuxAIO设计时就没考虑扩展性。

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

eBPF对用户是透明的,只需升级内核(到合适的版本),应用程序无需任何改造;

io_uring提供了新的系统调用和用户空间API,因此需要应用程序做改造。

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

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

以下是译文。

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

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

1LinuxI/O系统调用演进1.1基于fd的阻塞式I/O:read()/write()

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

ssize_tread(intfd,void*buf,size_tcount);ssize_twrite(intfd,constvoid*buf,size_tcount);

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

如果数据在文件中,并且文件内容已经缓存在pagecache中,调用会立即返回;

如果数据在另一台机器上,就需要通过网络(例如TCP)获取,会阻塞一段时间;

如果数据在硬盘上,也会阻塞一段时间。

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

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

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

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

1.3线程池方式

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

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

1.4DirectI/O(数据库软件):绕过pagecache

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

需要指定O_DIRECTflag;

需要应用自己管理自己的缓存——这正是数据库软件所希望的;

是zero-copyI/O,因为应用的缓冲数据直接发送到设备,或者直接从设备读取。

1.5异步IO(AIO)

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

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

用户通过io_submit()提交I/O请求,

过一会再调用io_getevents()来检查哪些events已经ready了。

使程序员能编写完全异步的代码。

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

Replyto:tosupportopeningfilesasynchronously[9]SoIthinkthisisridiculouslyugly.AIOisahorriblead-hocdesign,withthemainexcusebeing“other,lessgiftedpeople,madethatdesign,andweareimplementingitfor

转载请注明:http://www.0431gb208.com/sjszjzl/376.html