专栏电商日志财经减肥爱情
投稿投诉
爱情常识
搭配分娩
减肥两性
孕期塑形
财经教案
论文美文
日志体育
养生学堂
电商科学
头戴业界
专栏星座
用品音乐

Linux异步IO框架iouring基本原理程序示例与性能压

  iouring是2019年Linux5。1内核首次引入的高性能异步IO框架,能显着加速IO密集型应用的性能。但如果你的应用已经在使用传统LinuxAIO了,并且使用方式恰当,那iouring并不会带来太大的性能提升根据测试,即便打开高级特性,也只有5。除非你真的需要这5的额外性能,否则切换成iouring代价可能也挺大,因为要重写应用来适配iouring(或者让依赖的平台或框架去适配,总之需要改代码)。
  既然性能跟传统AIO差不多,那为什么还称iouring为革命性技术呢?
  1、它首先和最大的贡献在于:统一了Linux异步IO框架,LinuxAIO只支持directIO模式的存储文件(storagefile),而且主要用在数据库这一细分领域;iouring支持存储文件和网络文件(networksockets),也支持更多的异步系统调用(acceptopenatstat。。。),而非仅限于readwrite系统调用。
  2、在设计上是真正的异步IO,作为对比,LinuxAIO虽然也是异步的,但仍然可能会阻塞,某些情况下的行为也无法预测;
  3、灵活性和可扩展性非常好,甚至能基于iouring重写所有系统调用,而LinuxAIO设计时就没考虑扩展性。
  eBPF也算是异步框架(事件驱动),但与iouring没有本质联系,二者属于不同子系统,并且在模型上有一个本质区别:eBPF对用户是透明的,只需升级内核(到合适的版本),应用程序无需任何改造;iouring提供了新的系统调用和用户空间API,因此需要应用程序做改造。
  eBPF作为动态跟踪工具,能够更方便地排查和观测iouring等模块在执行层面的具体问题。
  本文介绍Linux异步IO的发展历史,iouring的原理和功能,并给出了一些程序示例和性能压测结果(我们在5。10内核做了类似测试)。
  以下是译文。
  很多人可能还没意识到,Linux内核在过去几年已经发生了一场革命。这场革命源于两个激动人心的新接口的引入:eBPF和iouring。我们认为,二者将会完全改变应用与内核交互的方式,以及应用开发者思考和看待内核的方式。
  本文介绍iouring(我们在ScyllaDB中有iouring的深入使用经验),并略微提及一下eBPF。1LinuxIO系统调用演进1。1基于fd的阻塞式IO:read()write()
  作为大家最熟悉的读写方式,Linux内核提供了基于文件描述符的系统调用,这些描述符指向的可能是存储文件(storagefile),也可能是networksockets:ssizetread(intfd,voidbuf,sizetcount);ssizetwrite(intfd,constvoidbuf,sizetcount);
  二者称为阻塞式系统调用(blockingsystemcalls),因为程序调用这些函数时会进入sleep状态,然后被调度出去(让出处理器),直到IO操作完成:如果数据在文件中,并且文件内容已经缓存在pagecache中,调用会立即返回;如果数据在另一台机器上,就需要通过网络(例如TCP)获取,会阻塞一段时间;如果数据在硬盘上,也会阻塞一段时间。
  但很容易想到,随着存储设备越来越快,程序越来越复杂,阻塞式(blocking)已经这种最简单的方式已经不适用了。1。2非阻塞式IO:select()poll()epoll()
  阻塞式之后,出现了一些新的、非阻塞的系统调用,例如select()、poll()以及更新的epoll()。应用程序在调用这些函数读写时不会阻塞,而是立即返回,返回的是一个已经ready的文件描述符列表。
  但这种方式存在一个致命缺点:只支持networksockets和pipesepoll()甚至连storagefiles都不支持。1。3线程池方式
  对于storageIO,经典的解决思路是threadpool〔5〕:主线程将IO分发给worker线程,后者代替主线程进行阻塞式读写,主线程不会阻塞。
  这种方式的问题是线程上下文切换开销可能非常大,后面性能压测会看到。1。4DirectIO(数据库软件):绕过pagecache
  随后出现了更加灵活和强大的方式:数据库软件(databasesoftware)有时并不想使用操作系统的pagecache〔6〕,而是希望打开一个文件后,直接从设备读写这个文件(directaccesstothedevice)。这种方式称为直接访问(directaccess)或直接IO(directIO),需要指定ODIRECTflag;需要应用自己管理自己的缓存这正是数据库软件所希望的;是zerocopyIO,因为应用的缓冲数据直接发送到设备,或者直接从设备读取。1。5异步IO(AIO)
  前面提到,随着存储设备越来越快,主线程和worker线性之间的上下文切换开销占比越来越高。现在市场上的一些设备,例如IntelOptane〔7〕,延迟已经低到和上下文切换一个量级(微秒us)。换个方式描述,更能让我们感受到这种开销:上下文每切换一次,我们就少一次dispatchIO的机会。
  因此,Linux2。6内核引入了异步IO(asynchronousIO)接口,方便起见,本文简写为linuxaio。AIO原理是很简单的:用户通过iosubmit()提交IO请求,过一会再调用iogetevents()来检查哪些events已经ready了。使程序员能编写完全异步的代码。
  近期,LinuxAIO甚至支持了〔8〕epoll():也就是说不仅能提交storageIO请求,还能提交网络IO请求。照这样发展下去,linuxaio似乎能成为一个王者。但由于它糟糕的演进之路,这个愿望几乎不可能实现了。我们从Linus标志性的激烈言辞中就能略窥一斑:
  Replyto:tosupportopeningfilesasynchronously〔9〕SoIthinkthisisridiculouslyugly。AIOisahorribleadhocdesign,withthemainexcusebeingother,lessgiftedpeople,madethatdesign,andweareimplementingitforcompatibilitybecausedatabasepeoplewhoseldomhaveanyshredoftasteactuallyuseit。LinusTorvalds(onlwn。net)
  首先,作为数据库从业人员,我们想借此机会为我们的没品(lackoftaste)向Linus道歉。但更重要的是,我们要进一步解释一下为什么Linus是对的:LinuxAIO确实问题缠身,只支持ODIRECT文件,因此对常规的非数据库应用(normal,nondatabaseapplications)几乎是无用的;接口在设计时并未考虑扩展性。虽然可以扩展我们也确实这么做了但每加一个东西都相当复杂;虽然从技术上说接口是非阻塞的,但实际上有很多可能的原因都会导致它阻塞〔10〕,而且引发的方式难以预料。1。6小结
  以上可以清晰地看出LinuxIO的演进:最开始是同步(阻塞式)系统调用;然后随着实际需求和具体场景,不断加入新的异步接口,还要保持与老接口的兼容和协同工作。
  另外也看到,在非阻塞式读写的问题上并没有形成统一方案:Networksocket领域:添加一个异步接口,然后去轮询(poll)请求是否完成(readiness);StorageIO领域:只针对某一细分领域(数据库)在某一特定时期的需求,添加了一个定制版的异步接口。
  这就是LinuxIO的演进历史只着眼当前,出现一个问题就引入一种设计,而并没有多少前瞻性直到iouring的出现。
  相关视频推荐
  iouring新起之秀的linuxio模式,是如何媲美epoll的
  linux下的epoll实战揭秘支撑亿级IO的底层基石
  学习地址:https:ke。qq。comcourse417774?flowToken1013300
  需要CCLinux服务器架构师学习资料加qun812855908获取(资料包括CC,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCPIP,协程,DPDK,ffmpeg等),免费分享
  2iouring
  iouring来自资深内核开发者JensAxboe的想法,他在LinuxIOstack领域颇有研究。从最早的patchaio:supportforIOpolling〔11〕可以看出,这项工作始于一个很简单的观察:随着设备越来越快,中断驱动(interruptdriven)模式效率已经低于轮询模式(pollingforcompletions)这也是高性能领域最常见的主题之一。iouring的基本逻辑与linuxaio是类似的:提供两个接口,一个将IO请求提交到内核,一个从内核接收完成事件。但随着开发深入,它逐渐变成了一个完全不同的接口:设计者开始从源头思考如何支持完全异步的操作。2。1与LinuxAIO的不同
  iouring与linuxaio有着本质的不同:在设计上是真正异步的(trulyasynchronous)。只要设置了合适的flag,它在系统调用上下文中就只是将请求放入队列,不会做其他任何额外的事情,保证了应用永远不会阻塞。支持任何类型的IO:cachedfiles、directaccessfiles甚至blockingsockets。由于设计上就是异步的(asyncbydesignnature),因此无需pollreadwrite来处理sockets。只需提交一个阻塞式读(blockingread),请求完成之后,就会出现在completionring。灵活、可扩展:基于iouring甚至能重写(reimplement)Linux的每个系统调用。2。2原理及核心数据结构:SQCQSQECQE
  每个iouring实例都有两个环形队列(ring),在内核和应用程序之间共享:提交队列:submissionqueue(SQ)完成队列:completionqueue(CQ)
  这两个队列:都是单生产者、单消费者,size是2的幂次;提供无锁接口(locklessaccessinterface),内部使用内存屏障做同步(coordinatedwithmemorybarriers)。
  使用方式:请求应用创建SQentries(SQE),更新SQtail;内核消费SQE,更新SQhead。完成内核为完成的一个或多个请求创建CQentries(CQE),更新CQtail;应用消费CQE,更新CQhead。完成事件(completionevents)可能以任意顺序到达,到总是与特定的SQE相关联的。消费CQE过程无需切换到内核态。2。3带来的好处
  iouring这种请求方式还有一个好处是:原来需要多次系统调用(读或写),现在变成批处理一次提交。
  还记得Meltdown漏洞吗?当时我还写了一篇文章〔12〕解释为什么我们的ScyllaNoSQL数据库受影响很小:aio已经将我们的IO系统调用批处理化了。
  iouring将这种批处理能力带给了storageIO系统调用之外的其他一些系统调用,包括:readwritesendrecvacceptopenatstat专用的一些系统调用,例如fallocate
  此外,iouring使异步IO的使用场景也不再仅限于数据库应用,普通的非数据库应用也能用。这一点值得重复一遍:
  虽然iouring与aio有一些相似之处,但它的扩展性和架构是革命性的:它将异步操作的强大能力带给了所有应用(及其开发者),而不再仅限于是数据库应用这一细分领域。
  我们的CTOAviKivity在theCoreC2019event上有一次关于async的分享〔13〕。核心点包括:从延迟上来说,现代多核、多CPU设备,其内部本身就是一个基础网络;CPU之间是另一个网络;CPU和磁盘IO之间又是一个网络。
  因此网络编程采用异步是明智的,而现在开发自己的应用也应该考虑异步。这从根本上改变了Linux应用的设计方式:之前都是一段顺序代码流,需要系统调用时才执行系统调用,现在需要思考一个文件是否ready,因而自然地引入eventloop,不断通过共享buffer提交请求和接收结果。2。4三种工作模式
  iouring实例可工作在三种模式:中断驱动模式(interruptdriven)默认模式。可通过iouringenter()提交IO请求,然后直接检查CQ状态判断是否完成。轮询模式(polled)BusywaitingforanIOcompletion,而不是通过异步IRQ(InterruptRequest)接收通知。这种模式需要文件系统(如果有)和块设备(blockdevice)支持轮询功能。相比中断驱动方式,这种方式延迟更低(连系统调用都省了〔14〕),但可能会消耗更多CPU资源。目前,只有指定了ODIRECTflag打开的文件描述符,才能使用这种模式。当一个读或写请求提交给轮询上下文(polledcontext)之后,应用(application)必须调用iouringenter()来轮询CQ队列,判断请求是否已经完成。对一个iouring实例来说,不支持混合使用轮询和非轮询模式。内核轮询模式(kernelpolled)
  这种模式中,会创建一个内核线程(kernelthread)来执行SQ的轮询工作。
  使用这种模式的iouring实例,应用无需切到到内核态就能触发(issue)IO操作。通过SQ来提交SQE,以及监控CQ的完成状态,应用无需任何系统调用,就能提交和收割IO(submitandreapIOs)。
  如果内核线程的空闲时间超过了用户的配置值,它会通知应用,然后进入idle状态。这种情况下,应用必须调用iouringenter()来唤醒内核线程。如果IO一直很繁忙,内核线性是不会sleep的。2。5iouring系统调用API
  有三个:iouringsetup(2)iouringregister(2)iouringenter(2)
  下面展开介绍。完整文档见manpage〔15〕。2。5。1iouringsetup()
  执行异步IO需要先设置上下文:intiouringsetup(u32entries,structiouringparamsp);
  这个系统调用创建一个SQ和一个CQ,queuesize至少entries个元素,返回一个文件描述符,随后用于在这个iouring实例上执行操作。
  SQ和CQ在应用和内核之间共享,避免了在初始化和完成IO时(initiatingandcompletingIO)拷贝数据。
  参数p:应用用来配置iouring,内核返回的SQCQ配置信息也通过它带回来。
  iouringsetup()成功时返回一个文件描述符(fd)。应用随后可以将这个fd传给mmap(2)系统调用,来mapthesubmissionandcompletionqueues或者传给totheiouringregister()oriouringenter()systemcalls。2。5。2iouringregister()
  注册用于异步IO的文件或用户缓冲区(filesoruserbuffers):intiouringregister(unsignedintfd,unsignedintopcode,voidarg,unsignedintnrargs);
  注册文件或用户缓冲区,使内核能长时间持有对该文件在内核内部的数据结构引用(internalkerneldatastructuresassociatedwiththefiles),或创建应用内存的长期映射(longtermmappingsofapplicationmemoryassociatedwiththebuffers),这个操作只会在注册时执行一次,而不是每个IO请求都会处理,因此减少了perIOoverhead。注册的缓冲区(buffer)性质Registeredbuffers将会被锁定在内存中(belockedinmemory),并计入用户的RLIMITMEMLOCK资源限制。此外,每个buffer有1GB的大小限制。当前,buffers必须是匿名、非文件后端的内存(anonymous,nonfilebackedmemory),例如malloc(3)ormmap(2)withtheMAPANONYMOUSflagset返回的内存。Hugepages也是支持的。整个hugepage都会被pin到内核,即使只用到了其中一部分。已经注册的buffer无法调整大小,想调整只能先unregister,再重新register一个新的。通过eventfd()订阅completion事件
  可以用eventfd(2)订阅iouring实例的completionevents。将eventfd描述符通过这个系统调用注册就行了。
  Thecredentialsoftherunningapplicationcanberegisteredwithiouringwhichreturnsanidassociatedwiththosecredentials。ApplicationswishingtosharearingbetweenseparateusersprocessescanpassinthiscredentialidintheSQEpersonalityfield。Ifset,thatparticularSQEwillbeissuedwiththesecredentials。2。5。3iouringenter()intiouringenter(unsignedintfd,unsignedinttosubmit,unsignedintmincomplete,unsignedintflags,sigsettsig);
  这个系统调用用于初始化和完成(initiateandcomplete)IO,使用共享的SQ和CQ。单次调用同时执行:提交新的IO请求等待IO完成
  参数:fd是iouringsetup()返回的文件描述符;tosubmit指定了SQ中提交的IO数量;依据不同模式:默认模式,如果指定了mincomplete,会等待这个数量的IO事件完成再返回;如果iouring是polling模式,这个参数表示:0:要求内核返回当前以及完成的所有events,无阻塞;非零:如果有事件完成,内核仍然立即返回;如果没有完成事件,内核会poll,等待指定的次数完成,或者这个进程的时间片用完。
  注意:对于interruptdrivenIO,应用无需进入内核就能检查CQ的eventcompletions。
  iouringenter()支持很多操作,包括:Open,close,andstatfilesReadandwriteintomultiplebuffersorpremappedbuffersSocketIOoperationsSynchronizefilestateAsynchronouslymonitorasetoffiledescriptorsCreateatimeoutlinkedtoaspecificoperationintheringAttempttocancelanoperationthatiscurrentlyinflightCreateIOchainsOrderedexecutionwithinachainParallelexecutionofmultiplechains
  当这个系统调用返回时,表示一定数量的SEQ已经被消费和提交了,此时可以安全的重用队列中的SEQ。此时IO提交有可能还停留在异步上下文中,即实际上SQE可能还没有被提交不过用户不用关心这些细节当随后内核需要使用某个特定的SQE时,它已经复制了一份。2。6高级特性
  iouring提供了一些用于特殊场景的高级特性:Fileregistration(文件注册):每次发起一个指定文件描述的操作,内核都需要花费一些时钟周期(cycles)将文件描述符映射到内部表示。对于那些针对同一文件进行重复操作的场景,iouring支持提前注册这些文件,后面直接查找就行了。Bufferregistration(缓冲区注册):与fileregistration类似,directIO场景中,内核需要mapunmapmemoryareas。iouring支持提前注册这些缓冲区(buffers)。Pollring(轮询环形缓冲区):对于非常快是设备,处理中断的开销是比较大的。iouring允许用户关闭中断,使用轮询模式。前面三种工作模式小节也介绍到了这一点。Linkedoperations(链接操作):允许用户发送串联的请求。这两个请求同时提交,但后面的会等前面的处理完才开始执行。2。7用户空间库liburing
  liburing〔16〕提供了一个简单的高层API,可用于一些基本场景,应用程序避免了直接使用更底层的系统调用。此外,这个API还避免了一些重复操作的代码,如设置iouring实例。
  举个例子,在iouringsetup()的manpage描述中,调用这个系统调用获得一个ring文件描述符之后,应用必须调用mmap()来这样的逻辑需要一段略长的代码,而用liburing的话,下面的函数已经将上述流程封装好了:intiouringqueueinit(unsignedentries,structiouringring,unsignedflags);
  下一节来看两个例子基于liburing的例子。3基于liburing的示例应用
  编译:gitclonehttps:github。comaxboeliburing。gitgitcobliburing2。0tagsliburing2。0cdliburinglsexamplesiouringcpiouringcp。ciouringtestiouringtest。clinkcplinkcp。cMakefileucontextcpucontextcp。cmakej4。examplesiouringtestfileSubmitted4,completed4,bytes16384。exampleslinkcpinfileoutfile3。1iouringtest
  这个程序使用4个SQE,从输入文件中读取最多16KB数据。源码及注释
  为方便看清主要逻辑,忽略了一些错误处理代码,完整代码见iouringtest。c〔17〕。SPDXLicenseIdentifier:MITSimpleappthatdemonstrateshowtosetupaniouringinterface,submitandcompleteIOagainstit,andthentearitdown。gccWallO2DGNUSOURCEoiouringtestiouringtest。cluringincludeliburing。hdefineQD4iouring队列长度intmain(intargc,charargv〔〕){inti,fd,pending,done;voidbuf;1。初始化一个iouring实例structiouringring;retiouringqueueinit(QD,队列长度ring,iouring实例0);flags,0表示默认配置,例如使用中断驱动模式2。打开输入文件,注意这里指定了ODIRECTflag,内核轮询模式需要这个flag,见前面介绍fdopen(argv〔1〕,ORDONLYODIRECT);structstatsb;fstat(fd,sb);获取文件信息,例如文件长度,后面会用到3。初始化4个读缓冲区ssizetfsize0;程序的最大读取长度structioveciovecscalloc(QD,sizeof(structiovec));for(i0;iQD;i){if(posixmemalign(buf,4096,4096))return1;iovecs〔i〕。iovbasebuf;起始地址iovecs〔i〕。iovlen4096;缓冲区大小fsize4096;}4。依次准备4个SQE读请求,指定将随后读入的数据写入iovecsstructiouringsqesqe;offset0;i0;do{sqeiouringgetsqe(ring);获取可用SQEiouringprepreadv(sqe,用这个SQE准备一个待提交的read操作fd,从fd打开的文件中读取数据iovecs〔i〕,iovec地址,读到的数据写入iovec缓冲区1,iovec数量offset);读取操作的起始地址偏移量offsetiovecs〔i〕。iovlen;更新偏移量,下次使用i;if(offsetsb。stsize)如果超出了文件大小,停止准备后面的SQEbreak;}while(1);5。提交SQE读请求retiouringsubmit(ring);4个SQE一次提交,返回提交成功的SQE数量if(ret0){fprintf(stderr,iouringsubmit:s,strerror(ret));return1;}elseif(ret!i){fprintf(stderr,iouringsubmitsubmittedlessd,ret);return1;}6。等待读请求完成(CQE)structiouringcqecqe;done0;pendingret;fsize0;for(i0;ipending;i){iouringwaitcqe(ring,cqe);等待系统返回一个读完成事件done;if(cqeres!4096cqeresfsize!sb。stsize){fprintf(stderr,retd,wanted4096,cqeres);}fsizecqeres;iouringcqeseen(ring,cqe);更新iouring实例的完成队列}7。打印统计信息printf(Submittedd,completedd,byteslu,pending,done,(unsignedlong)fsize);8。清理工作close(fd);iouringqueueexit(ring);return0;}其他说明
  代码中已经添加了注释,这里再解释几点:每个SQE都执行一个allocatedbuffer,后者是用iovec结构描述的;第34步:初始化所有SQE,用于接下来的IORINGOPREADV操作,后者提供了readv(2)系统调用的异步接口。操作完成之后,这个SQEiovecbuffer中存放的是相关readv操作的结果;接下来调用iouringwaitcqe()来reapCQE,并通过cqeres字段验证读取的字节数;iouringcqeseen()通知内核这个CQE已经被消费了。3。2linkcp
  linkcp使用iouring高级特性SQEchaining特性来复制文件。IOchain
  iouring支持创建IOchain。一个chain内的IO是顺序执行的,多个IOchain可以并行执行。
  iouringenter()manpage中对IOSQEIOLINK有详细解释〔18〕:
  Whenthisflagisspecified,itformsalinkwiththenextSQEinthesubmissionring。ThatnextSQEwillnotbestartedbeforethisonecompletes。This,ineffect,formsachainofSQEs,whichcanbearbitrarilylong。ThetailofthechainisdenotedbythefirstSQEthatdoesnothavethisflagset。ThisflaghasnoeffectonpreviousSQEsubmissions,nordoesitimpactSQEsthatareoutsideofthechaintail。Thismeansthatmultiplechainscanbeexecutinginparallel,orchainsandinpidualSQEs。Onlymembersinsidethechainareserialized。AchainofSQEswillbebroken,ifanyrequestinthatchainendsinerror。iouringconsidersanyunexpectedresultanerror。Thismeansthat,eg,ashortreadwillalsoterminatetheremainderofthechain。IfachainofSQElinksisbroken,theremainingunstartedpartofthechainwillbeterminatedandcompletedwithECANCELEDastheerrorcode。Availablesince5。3。
  为实现复制文件功能,linkcp创建一个长度为2的SQEchain。第一个SQE是一个读请求,将数据从输入文件读到buffer;第二个请求,与第一个请求是linked,是一个写请求,将数据从buffer写入输出文件。源码及注释SPDXLicenseIdentifier:MITVerybasicproofofconceptfordoingacopywithlinkedSQEs。Needsabitoferrorhandlingandshortreadlove。includeliburing。hdefineQD64iouring队列长度defineBS(321024)structiodata{sizetoffset;intindex;structioveciov;};staticintinfd,outfd;staticunsignedinflight;创建一个readwriteSQEchainstaticvoidqueuerwpair(structiouringring,offtsize,offtoffset){structiouringsqesqe;structiodatadata;voidptr;ptrmalloc(sizesizeof(data));dataptrsize;dataindex0;dataoffsetoffset;dataiov。iovbaseptr;dataiov。iovlensize;sqeiouringgetsqe(ring);获取可用SQEiouringprepreadv(sqe,infd,dataiov,1,offset);准备read请求sqeflagsIOSQEIOLINK;设置为LINK模式iouringsqesetdata(sqe,data);设置datasqeiouringgetsqe(ring);获取另一个可用SQEiouringprepwritev(sqe,outfd,dataiov,1,offset);准备write请求iouringsqesetdata(sqe,data);设置data}处理完成(completion)事件:释放SQE的内存缓冲区,通知内核已经消费了CQE。staticinthandlecqe(structiouringring,structiouringcqecqe){structiodatadataiouringcqegetdata(cqe);获取CQEdataindex;if(cqeres0){if(cqeresECANCELED){queuerwpair(ring,BS,dataoffset);inflight2;}else{printf(cqeerror:s,strerror(cqeres));ret1;}}if(dataindex2){readwritechain完成,释放缓冲区内存voidptr(void)datadataiov。iovlen;free(ptr);}iouringcqeseen(ring,cqe);通知内核已经消费了CQE事件returnret;}staticintcopyfile(structiouringring,offtinsize){structiouringcqecqe;sizetthissize;offtoffset;offset0;while(insize){数据还没处理完inthasinflightinflight;当前正在进行中的SQE数量intdepth;SQE阈值,当前进行中的SQE数量(inflight)超过这个值之后,需要阻塞等待CQE完成while(insizeinflightQD){数据还没处理完,iouring队列也还没用完thissizeBS;if(thissizeinsize)最后一段数据不足BS大小thissizeinsize;queuerwpair(ring,thissize,offset);创建一个readwritechain,占用两个SQEoffsetthissize;insizethissize;inflight2;正在进行中的SQE数量2}if(hasinflight!inflight)如果有新创建的SQE,iouringsubmit(ring);就提交给内核if(insize)如果还有data等待处理,depthQD;阈值设置SQ的队列长度,即SQ队列用完才开始阻塞等待CQE;elsedata处理已经全部提交,depth1;阈值设置为1,即只要还有SQE未完成,就阻塞等待CQE下面这个while只有SQ队列用完或data全部提交之后才会执行到while(inflightdepth){如果所有SQE都已经用完,或者所有datareadwrite请求都已经提交iouringwaitcqe(ring,cqe);等待内核completion事件handlecqe(ring,cqe);处理completion事件:释放SQE内存缓冲区,通知内核CQE已消费inflight;正在进行中的SQE数量1}}return0;}staticintsetupcontext(unsignedentries,structiouringring){iouringqueueinit(entries,ring,0);return0;}staticintgetfilesize(intfd,offtsize){structstatst;if(fstat(fd,st)0)return1;if(SISREG(st。stmode)){sizest。stsize;return0;}elseif(SISBLK(st。stmode)){unsignedlonglongbytes;if(ioctl(fd,BLKGETSIZE64,bytes)!0)return1;sizebytes;return0;}return1;}intmain(intargc,charargv〔〕){structiouringring;offtinsize;intret;infdopen(argv〔1〕,ORDONLY);outfdopen(argv〔2〕,OWRONLYOCREATOTRUNC,0644);if(setupcontext(QD,ring))return1;if(getfilesize(infd,insize))return1;retcopyfile(ring,insize);close(infd);close(outfd);iouringqueueexit(ring);returnret;}其他说明
  代码中实现了三个函数:copyfile():高层复制循环逻辑;它会调用queuerwpair(ring,thissize,offset)来构造SQEpair;并通过一次iouringsubmit()调用将所有构建的SQEpair提交。这个函数维护了一个最大DQ数量的inflightSQE,只要数据copy还在进行中;否则,即数据已经全部读取完成,就开始等待和收割所有的CQE。queuerwpair()构造一个readwriteSQEpair。readSQE的IOSQEIOLINKflag表示开始一个chain,writeSQE不用设置这个flag,标志着这个chain的结束。用户data字段设置为同一个data描述符,并且在随后的completion处理中会用到。handlecqe()从CQE中提取之前由queuerwpair()保存的data描述符,并在描述符中记录处理进展(index)。如果之前请求被取消,它还会重新提交readwritepair。一个CQEpair的两个member都处理完成之后(index2),释放共享的datadescriptor。最后通知内核这个CQE已经被消费。4iouring性能压测(基于fio)
  对于已经在使用linuxaio的应用,例如ScyllaDB,不要期望换成iouring之后能获得大幅的性能提升,这是因为:iouring性能相关的底层机制与linuxaio并无本质不同(都是异步提交,轮询结果)。
  在此,本文也希望使读者明白:iouring首先和最重要的贡献在于:将linuxaio的所有优良特性带给了普罗大众(而非局限于数据库这样的细分领域)。4。1测试环境
  本节使用fio测试4种模式:synchronousreadsposixaio(implementedasathreadpool)linuxaioiouring
  硬件:NVMe存储设备,物理极限能打到3。5MIOPS。8核处理器4。2场景一:directIO1KB随机读(绕过pagecache)
  第一组测试中,我们希望所有的读请求都能命中存储设备(allreadstohitthestorage),完全绕开操作系统的页缓存(pagecache)。
  测试配置:8个CPU执行72fiojob,每个job随机读取4个文件,iodepth8(numberofIOunitstokeepinflightagainstthefile。)。
  这种配置保证了CPU处于饱和状态,便于观察IO性能。如果CPU数量足够多,那每组测试都可能会打满设备带宽,结果对IO压测就没意义了。
  表1。DirectIO(绕过系统页缓存):1KB随机读,CPU100下的IO性能
  backend
  IOPS
  contextswitches
  IOPSvsiouring
  sync
  814,000
  27,625,004
  42。6
  posixaio(threadpool)
  433,000
  64,112,335
  69。4
  linuxaio
  1,322,000
  10,114,149
  6。7
  iouring(basic)
  1,417,000
  11,309,574
  iouring(enhanced)
  1,486,000
  11,483,468
  4。9
  几点分析:iouring相比linuxaio确实有一定提升,但并非革命性的。开启高级特性,例如bufferfileregistration之后性能有进一步提升但也还没有到为了这些性能而重写整个应用的地步,除非你是搞数据库研发,想榨取硬件的最后一分性能。iouringandlinuxaio都比同步read接口快2倍,而后者又比posixaio快2倍初看有点差异。但看看上下文切换次数,就不难理解为什么posixaio这么慢了。同步read性能差是因为:在这种没有pagecache的情况下,每次read系统调用都会阻塞,因此就会涉及一次上下文切换。posixaio性能更差是因为:不仅内核和应用程序之间要频繁上下文切换,线程池的多个线程之间也在频繁切换。4。2场景二:bufferedIO1KB随机读(数据提前加载到内存,100hotcache)
  第二组测试bufferedIO:将文件数据提前加载到内存,然后再测随机读。由于数据全部在pagecache,因此同步read永远不会阻塞。这种场景下,我们预期同步读和iouring的性能差距不大(都是最好的)。其他测试条件不变。
  表2。BufferedIO(数据全部来自pagecache,100hotcache):1KB随机读,100CPU下的IO性能
  Backend
  IOPS
  contextswitches
  IOPSvsiouring
  sync
  4,906,000
  105,797
  2。3
  posixaio(threadpool)
  1,070,000
  114,791,187
  78。7
  linuxaio
  4,127,000
  105,052
  17。9
  iouring
  5,024,000
  106,683
  结果分析:同步读和iouring性能差距确实很小,二者都是最好的。但注意,实际的应用不可能一直100时间执行IO操作,因此基于同步读的真实应用性能还是要比基于iouring要差的,因为iouring会将多个系统调用批处理化。posixaio性能最差,直接原因是上下文切换次数太多,这也和场景相关:在这种CPU饱和的情况下,它的线程池反而是累赘,会完全拖慢性能。linuxaio并不是针对bufferedIO设计的,在这种pagecache直接返回的场景,它的异步接口反而会造成性能损失将操作分为dispatch和consume两步不但没有性能收益,反而有额外开销。4。3性能测试小结
  最后再次提醒,本节是极端应用场景(100CPU100cachemisshit)测试,真实应用的行为通常处于同步读和异步读之间:时而一些阻塞操作,时而一些非阻塞操作。但不管怎么说,用了iouring之后,用户就无需担心同步和异步各占多少比例了,因为它在任何场景下都表现良好。如果操作是非阻塞的,iouring不会有额外开销;如果操作是阻塞式的,也没关系,iouring是完全异步的,并且不依赖线程池或昂贵的上下文切换来实现这种异步能力;
  本文测试的都是随机读,但对其他类型的操作,iouring表现也是非常良好的。例如:打开关闭文件设置定时器通过networksockets传输数据
  而且使用的是同一套iouring接口。4。4ScyllaDB与iouring
  Scylla重度依赖directIO,从一开始就使用linuxaio。在我们转向iouring的过程中,最开始测试显示对某些workloads,能取得50以上的性能提升。但深入研究之后发现,这是因为我们之前的linuxaio用的不够好。这也揭示了一个经常被忽视的事实:获得高性能没有那么难(前提是你得弄对了)。在对比iouring和linuxaio应用之后,我们很快改进了一版,二者的性能差距就消失了。但坦率地说,解决这个问题需要一些工作量,因为要改动一个已经使用了很多年的基于linuxaio的接口。而对iouring应用来说,做类似的改动是轻而易举的。
  以上只是一个场景,iouring相比linuxaio的优势是能应用于fileIO之外的场景。此外,它还自带了特殊的高性能〔19〕接口,例如bufferregistration、fileregistration、轮询模式等等。
  启用iouring高级特性之后,我们看到性能确实有提升:IntelOptane设备,单个CPU读取512字节,观察到5的性能提升。与表12对得上。虽然5的提升看上去不是太大,但对于希望压榨出硬件所有性能的数据库来说,还是非常宝贵的。
  5eBPF
  eBPF也是一个事件驱动框架(因此也是异步的),允许用户空间程序动态向内核注入字节码,主要有两个使用场景:NetworkingTracingObservability:例如bcc〔21〕等工具
  eBPF在内核4。9首次引入,4。19以后功能已经很强大。
  谈到与iouring的结合,就是用bcc之类的工具跟踪一些IO相关的内核函数,例如:Tracehowmuchtimeanapplicationspendssleeping,andwhatledtothosesleeps。(wakeuptime)Findallprogramsinthesystemthatreachedaparticularplaceinthecode(trace)AnalyzenetworkTCPthroughputaggregatedbysubnet(tcpsubnet)Measurehowmuchtimethekernelspentprocessingsoftirqs(softirqs)Captureinformationaboutallshortlivedfiles,wheretheycomefrom,andforhowlongtheywereopened(filelife)6结束语
  iouring和eBPF这两大特性将给Linux编程带来革命性的变化。有了这两个特性的加持,开发者就能更充分地利用Amazoni3enmeganodesystems〔22〕之类的多核多处理器系统,以及IntelOptane持久存储〔23〕之类的us级延迟存储设备。

2021年感恩有您!2022年咱们相约一起走吧不知不觉,2021年就这么结束了,回头一想,新冠也两年了。2021年充满变数,零星疫情此起彼伏,几次大规模疫情让全国的旅游业乱成一锅粥,也打断了我们的计划。疫情两年了,我们的旅行计冬季穿靴子,究竟买短还是买长?看完潮人街拍我懂了或许大家都听过时尚是不分季节这么一句话,但是一到换季,市面上的单品还是展现出非常分明的季节色彩。就拿鞋子来说吧,一到冬季,单鞋高跟鞋都进入了鞋柜最底层,取代这些的是靴子。靴子基本都安利是传销?还是直销?还是割韭菜?安利是一家非上市公司,它没有披露美国监管机构的任何调查。但是,它在中国目前是被认可的合法的直销公司,这个问题困惑我很久?据数据统计安利过去10年的增长都依赖于中国,中国目前是安利最备战LPL电竞春晚,BLG小狗提高Rank强度,RNG小明恢复训练时光荏苒,2022年悄然而至,小伙伴们新年快乐。随着2022年的到来,沉寂已久的LPLLCK联赛重燃战火,包括EDGRNGWBGTESFPXIGLNGT1DKGEN在内的数十支LODNF1月20号春节版本曝光,百级转职书回归,新职业与魂异界上线12月31日的体验服终于开始爆料1月20的春节版本的内容,目前主要包括阿拉德探险记8阿拉德华丽仓库礼包魂异界冲锋不渝之咏商店回归活动合金战士作战计划心悦宠物等。阿拉德探险记8奖励跟家长会小总结学校在接近元旦时开家长会了,本来是我去的,但是老公昨晚值班,今天就在家了,看着他想参加,我就把家长会一个人的名额给他了。开会也是参观幼儿园宝宝们的一天,从八点半去一直到两点半才结束广东男篮与辽宁队比赛结束后,日媒透露这3人实力有望超八村塁北京时间1月1日,CBA赛场迎来一场焦点之战,那就是上赛季冠军广东男篮迎战亚军辽宁男篮,在这场比赛结束后,有关比赛的报道非常多,但其中一条消息,引起了球迷的注意。有一家日本媒体透露易建联3分郭艾伦22分,赵继伟完爆徐杰,辽篮整体实力狂胜广东20212022赛季CBA联赛第二阶段的元旦大战迎来了辽篮与广东大益茶男篮的比赛,辽篮本赛季兵强马壮,阵容厚度联盟第一,当然得到的结果也是联盟排名第一,而广东大益茶男篮本赛季遭遇到广东20分惨败!单节10分阿联6中0被防死,辽篮三核心60分17助8断北京时间1月1日,CBA常规赛焦点战辽宁VS广东!本场比赛最终广东95115遭遇惨败,辽宁用联防破坏了广东的进攻体系,内线和锋线占据运动能力和活力上的绝对优势,张镇麟和大韩付豪连续缺少马尚的广东已不在是广东虽然广东球迷最不愿承认广东依赖外援,但现实不由得你不承认,本赛季的广东相比以前几个赛季基本不输球的广东基本上什么都不差,多了个易建联少了个马尚,但成绩却一落千丈。感觉任何对手都可以成了!皇马将顶薪合同签下姆巴佩,哈兰德暗示也将转会皇马对于皇马来说,原本这个赛季被认为是一个过渡性的赛季,然而从球队在赛季前半程的发挥来看,他们的表现已经明显超出了预期。在联赛中,他们高居积分榜第一位,领先第二名球队的分数已经达到了8
老人抱小孩在长沙地铁车厢内小便引热议!2月7日,有网友晒出图片一名老人抱着孩子蹲在长沙地铁6号线的车厢内,孩子正在小便。网友配文小孩子(看起来是读)幼儿园的年纪。老人带小孩子小便完就立马下地铁了,出地铁走半分钟就有卫生陕西省大众乒乓球公开赛挥拍开赛2月11日,乒动三秦陕西省大众乒乓球公开赛海盛智通杯宝鸡凤县分站赛开拍。来自全省18个代表队150余名乒乓球爱好者参赛。本次比赛由陕西省体育局主办,陕西省乒乓球羽毛球网球运动管理中陕西省首届三人篮球联赛行业组在延安启动陕西省首届三人篮球联赛行业组在延安启动。西部网讯(记者兰逊鸽)2月11日,陕西省首届延长石油杯三人篮球联赛行业组启动仪式暨延安赛区启动仪式在延安市举行。陕西省首届三人篮球联赛行业组乒坛消息!陈梦晋升教练员,欧洲名将被重罚,女队成绩一落千丈陈梦晋升教练员刚刚,山东体育局公布了一份关于职称评定名单,奥运冠军陈梦的名字赫然在列,拿到国家教练员的称号。作为国乒唯一获得殊荣的球员,这是对她职业生涯的又一肯定,东京奥运会上带领内蒙古首单跨境电商零售进口商品条码成功启用图为海关关员对内蒙古首单跨境电商零售进口商品条码商品进行查验。马鸿南摄中新网满洲里2月11日电(记者李爱平)满洲里海关11日对外消息指,内蒙古自治区首单跨境电商零售进口商品条码已启最新!2月7日起,太原古县城恢复免费入园太原古县城第二届锦绣太原中国年凤舞龙城花灯会已接近尾声,自2023年2月7日起,太原古县城景区恢复免费入园。自2023年1月20日启动至今,太原古县城累计接待游客63万余人,演出场老街灯如昼,小城夜未央!昨晚,郧西县城这个地方美如画!赏千灯万盏猜趣味灯谜正月十五元宵节当晚城关镇老北街张灯结彩热闹非凡市民纷纷前往赏花灯品民俗感受祥和喜庆的节日氛围制作精美,寓意美好市民徜徉在花灯下和家人一起赏花灯拍合影感受传统花灯回归理性消费的年轻人放下过度消费执念,有人断舍离,有人回归小县城ziV参加艺术展红星新闻记者李毅达蓝婧实习生宋佳旻责编官莉编辑潘莉这届年轻人开始回归理性消费了,豆瓣上活跃着这样一个小组消费降级,36万组员在里面热烈地讨论理性消费的话题。在这一小节庆过后,民俗消费如何长红?市民和游客在夫子庙东牌楼路逛灯市选花灯,兔子灯最受青睐。南京日报紫金山新闻记者段仁虎摄南京日报紫金山新闻记者黄琳燕今年春节,随着消费市场的全面复苏,传统民俗消费也格外红火。云锦博物塔克拉玛干沙漠南缘有个小三亚热闹时一天接待超3000名游客游客们在小三亚合影自拍。赵飞摄石榴云新疆日报记者刘一鸣棚外寒意未消,棚内春意盎然百余种热带果树和花卉开放正盛。前来游玩的孩子褪去厚重的冬衣,好奇地围在一棵芒果树旁细细观察。这树在咱四川阿坝童话公路自驾攻略头条创作挑战赛这期菲夜介绍一条阿坝童话公路自驾线路,这里有中国的阿尔卑斯山四姑娘山,人间瑶池黄龙,魔兽世界莲宝叶则,世界上海拔最高的咖啡厅达古冰川等等。阿坝童话公路路线图第一天从成
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网