目录
升级内核以支持io_uring
Io_uring 关注点
有序性
IOPOLL
SQPOLL
环大小
wrk线程数量
#!/bin/bash
#内核源码压缩包
kernel_targz="linux-5.14.21.tar.xz"
#内核源码解压后的目录
kernel_source="linux-5.14.21"
echo "解压${kernel_targz}中..."
xz -d -k ${kernel_targz}
cd ${kernel_source}
dnf -y install bison bc elfutils-devel make gcc-c++ flex openssl-devel dwarves rpmdevtools rsync
cp -v /boot/config-$(uname -r) .config
echo "CONFIG_IO_URING=y" >> .config
echo "CONFIG_TCM_QLA2XXX=y" >> .config
sed -ri 's,(CONFIG_SYSTEM_TRUSTED_KEYS=).*,\1"",' .config
#make
make binrpm-pkg -j 10
通常sqe是独立使用的,这意味着一个sqe的执行不会影响环中后续sqe项的执行或排序, 使它们能够并行执行和完成,以获得最大的效率和性能
io_uring支持排出提交端队列,直到之前的所有完成都完成为止。这允许应用程序将上述同步操作排入队列,并知道在之前的所有命令都完成之前,它不会启动。这是通过在sqe标志字段中设置IOSQUE_IO_DRAIN来实现的
虽然IOSQUE_IO_DRAIN包括一个完整的管道屏障,但IO_uring还支持更细粒度的sqe序列控制, 其中每个sqe的执行取决于前一个sqe的成功完成, 要使用此功能,如果前一个sqe没有完全完成,则链将断开,链接的sqe将被取消应用程序必须在sqe标志字段中设置IOSQUE_IO_LINK, 如果设置,则在上一个sqe成功完成之前,不会启动下一个sqe。
Polling
•主要节省来自于避免执行上下文切换
–成本可能有所不同,但通常为0.5~2us或更多。。。
•与IRQ相比,延迟的相对改善取决于设备的典型命令执行时间
–对于命令服务时间为毫秒或更长的磁盘驱动器,轮询毫无意义
•显然,对于速度极快的闪存SSD和新兴的NVM设备,轮询变得非常有趣
–设备访问延迟接近或低于上下文切换成本这取决于您的系统和存储设备
要使用IO轮询,必须在传递给io_uring_setup(2)系统调用或传递给io_uring_queue_init(3)库帮助程序的标志中设置IORING_SETUP_IOPOLL。当使用轮询时,应用程序不能再检查CQ环尾是否有完成,因为不会有自动触发的异步硬件端完成事件。相反,应用程序必须通过调用io_uring_enter(2)来主动查找和获取这些事件,其中IORING_ENTER_GETEVENTS集和min_complete集为所需的事件数。IORING_ENTER_GETEVENTS设置和min_complete设置为0是合法的。对于轮询IO,这要求内核简单地检查驱动程序端的完成事件,而不是不断地循环这样做。
要使用内核轮询功能,必须向io_uring_params标志成员特定的IORING_SETUP_SQPOLL注册io_uring实例,或者将其传递给io_uring_queue_init(3)。此外,如果应用程序希望将此线程限制为特定的CPU,也可以通过标记IORING_SETUP_SQ_AFF(默认情况下,io_uring创建的异步工作进程将继承其父进程的CPU掩码。这通常是系统中的所有CPU,除非父CPU使用有限的集合运行。如果这不是期望的结果,那么应用程序可能会明确地告诉io_uring异步工作程序可以在哪些CPU上运行)并将io_uring_params SQ_thread_CPU设置为所需的CPU来实现。请注意,使用IORING_SETUP_SQPOLL设置io_uring实例是一项特权操作。如果用户没有足够的权限,io_uring_queue_init(3)将以-EPERM失败。
为了避免在io_uring实例处于非活动状态时浪费太多CPU,内核端线程将在空闲一段时间后自动进入睡眠状态。当这种情况发生时,线程将在SQ环标志成员中设置IORING_SQ_NEED_WAKEUP。设置好后,应用程序不能依赖内核自动查找新条目,然后必须使用IORING_ENTER_SQ_WAKEUP集调用io_uring_enter(2)。应用程序端逻辑通常如下所示:
/*
* need to call io_uring_enter() to make the kernel notice the new IO
* if polled and the thread is now sleeping.
*/
if ((*sqring→flags) & IORING_SQ_NEED_WAKEUP)
{
io_uring_enter(ring_fd, to_submit, to_wait, IORING_ENTER_SQ_WAKEUP);
}
只要应用程序继续驱动IO,IORING_SQ_NEED_WAKEUP就永远不会被设置,我们可以在不执行单个系统调用的情况下有效地执行IO。然而,重要的是在应用程序中始终保持与上述类似的逻辑,以防线程进入睡眠状态。空闲前的特定宽限期可以通过设置io_uring_params sq_thread_idle成员来配置。该值以毫秒为单位。如果未设置此成员,内核将默认为线程进入睡眠状态之前的一秒钟空闲时间。
默认情况下,io_uring将创建的无边界工作者限制为 RLIMIT_NPROC(6)设置的最大处理器计数,并且有边界工作者是SQ环大小和系统中CPU数量的函数。有时,这可能会过多(或过少,对于有界),而此命令提供了一种更改每个环(每个NUMA节点)计数的方法。
arg必须设置为指向两个值的数组的无符号int指针,其中数组中的值设置为每个NUMA节点的最大工作者数。索引0包含有界工作者计数,索引1包含无界工作者计数。成功返回时,传入的数组将包含每个类型以前的最大valyes。如果传入的计数为0,则该命令返回当前最大值,并且不修改当前设置。nr_args必须设置为2,因为该命令有两个值。
存在多个工作线程的情况下,将创建多少个工作线程?
这取决于程序是单线程还是多线程。
io_urings在单线程情况下,如果主线程创建两个io_urings,并将每个io_uring配置为最多有两个未绑定的工作线程,则总共将生成两个工作线程
而在多线程程序的情况下,两个线程每创建一个io_uring,每个环最多有两个未绑定的工作线程总共将生成四个工作线程 - 每个程序线程两个。这反映在辅助角色名称 () 中的所有者线程 ID 中
每个线程都有自己的专用 I/O 工作线程池,为该线程操作的所有实例提供服务
设置会失败