Epoll与Io_uring在Linux中的对比
首先,我想告诉你我是如何走到这一步的,以及为什么我开始研究在Linux上处理异步I/O的不同选项……去年,我和我的学生们构建了一个名为TinyGate的反向代理服务器。它非常简单,基于工作者模型,基本上运作良好。当然,我并没有指望它会很快,但这是一个教育项目,既然我们制作了一个真的有点像生产级的工具,我为此感到非常自豪。但我的学生们并没有像我一样高兴——他们想构建一些真正有用的东西,他们对我们的“产品”有强大的架构限制,无法超越nginx和haproxy感到非常失望。因此,他们几乎逼迫我一起研究这些工具是如何在幕后工作以及如何处理异步I/O以减少大量开销……长话短说,我们做了第二版TinyGate,基于epoll。尽管在基准测试中仍然输给了nginx/haproxy,但与第一版相比,它的性能有了显著提升。但是epoll也并不完美(如我在下面将解释的),最终我们切换到了io_uring,这导致我们的项目再次从头开始完全重写……所以这是一个非常有趣的话题,今天我将分享Linux提供的用于异步I/O的两种排队系统的概述。 epoll的背景 当我刚开始为Linux开发时,epoll是一个新特性,基本上没有替代方案。每个人都使用它来管理异步执行——没有其他选择。问题是,epoll在很大程度上依赖于系统调用:它告诉你何时可以进行I/O,但你仍然必须在之后调用read()/write()——这意味着每个I/O事件需要两个系统调用,加上一个一次性的epoll_ctl注册。每个系统调用都会在用户模式和内核模式之间产生上下文切换,当你处理大量连接时,会造成巨大的开销。但我们有解决方案!在epoll进入Linux内核17年后(2002),io_uring在2019年出现了!它不是告诉你何时可以进行I/O,而是告诉你何时完成I/O——没有轮询循环,且相关的系统调用大大减少。内核从与您的应用程序和内核共享的内存中消费提交,并将完成结果发送回同一共享内存——二者都存在于环形缓冲区中,因此得名。 一个问题是:默认情况下,你仍然需要调用io_uring_enter()来告诉内核“去检查提交队列”——但一次调用可以提交一整批操作并获取一整批完成,而不是像epoll + read那样每个操作一个系统调用对。如果你想在稳定状态下接近零的系统调用,可以使用IORING_SETUP_SQPOLL,这会启动一个专用的内核线程为你轮询提交队列——代价是该线程会消耗CPU(下面会详细说明)。 一点比较 基本架构:如我之前所说,epoll在I/O可能时通知你,而io_uring会在I/O完成时通知你。在epoll中,每个I/O操作都要跨越内核边界,而io_uring则允许你一次支付一个小的“设置费”(创建环形队列),加上每批的费用(io_uring_enter()调用),而不是按操作收费。因此,你得到每批I/O一个系统调用,而不是每个I/O一对系统调用——或者,在SQPOLL情况下几乎没有。当你进行大量I/O时,这节省了很多系统调用。在支持io_uring的相对新系统上(内核v5.1+,2019年发布),通常没有太多理由去使用epoll。从就绪模型转向完成模型是一个巨大的架构变化——它将很大一部分工作从你的应用程序移到了内核中。 让我们编码! 当然,我不会让你没有代码演示这两个系统是如何工作的。我们将使用C语言。 (io_uring示例使用liburing,这是用户空间的辅助库——通过liburing-dev / liburing-devel安装,或者如果你想要零依赖,可以直接使用原始的io_uring_setup / io_uring_enter系统调用。) epoll 让我们演示一下epoll是如何工作的。我们将创建实例,注册一个文件描述符(在我们的例子中是标准输入),并处理传入的事件。正如你所看到的,这个示例总共使用了三个系统调用:epoll_ctl(一次性注册),然后是epoll_wait和read用于处理事件——因此每个实际的I/O事件需要两个系统调用,就像我上面提到的那样。代码本身相当容易理解。 io_uring 现在让我们用io_uring而不是epoll来做同样的事情。我们可以看到什么?类似的实例创建步骤。不需要epoll_ctl注册步骤。在提交前不需要就绪检查。完成时不需要单独的read()调用。是的,io_uring为此消耗的资源要少得多——不过,如上所述,除非你使用SQPOLL,否则在io_uring_submit()和io_uring_wait_cqe()里面仍然隐藏着一个io_uring_enter()调用。当你测试这些示例时,请记住为了简单起见,某些重要部分缺失。例如,如果标准输入没有任何输入,代码将永远阻塞。
本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。
☕请我喝杯咖啡