异步io linux,linux版软件网站

大家好,今天小编来为大家解答以下的问题,关于异步io linux,linux版软件网站这个很多人还不知道,现在让我们一起来看看吧!

linux下的异步io(aio)是否已成熟

io uring成为 Linux异步 IO的首选,当前版本已成熟稳定,但需注意在 5.12以前的内核中使用 io uring需调整 locked memory limit(RLIMIT_MEMLOCK)。libaio虽然作为古代 Linux的异步 IO解决方案,仅支持 direct IO操作。而 posix aio,作为 Glibc中的实现,自 1999年 Glibc-2.1版本以来就已支持,其内部使用线程池进行操作,导致性能有所牺牲,并且该实现中存在已知 bug。

MyTopling是一个基于 ToplingDB的 MySQL分叉,旨在复用 MyRocks的成果,而 ToplingDB又是基于 RocksDB的改进。在 ToplingDB的底层库 topling-zip中,通过 fiber+ io uring实现了高效的 MultiGet IO并发,为 MyTopling的 MRR(Multi Range Read)提供了强大的支撑。尽管 io uring已经存在数年,但在实际应用中仍有许多旧内核未更新至支持 io uring的版本,故 posix aio成为了这些环境下的替代选择。

posix aio最初定义于 POSIX.1-2001标准,支持多变体,其在 Glibc中的实现从 1999年 Glibc-2.1版本开始,一直沿用至今,其核心使用线程池结构。在 topling-zip的早期阶段,虽然支持 posix aio,但实际应用中并未采用。MyTopling中则将 posix aio整合,测试结果显示性能略逊于 io uring,但在其他方面一切正常。

在 CentOS 7.3系统上,直接使用 MyTopling进行测试时,遇到了问题,表现为系统崩溃且崩溃现场信息混乱。在排查过程中,发现 glibc的线程池最终调用的是 pread函数,而 pread函数本身并未出现问题。因此,问题最有可能出现在 glibc的 posix aio实现上,尽管该实现自 1999年以来就已存在,与标准发布时间相隔数年,但考虑到该实现的长时间稳定运行,该 bug应该已经被修复。

在深入分析和优化代码时,发现 posix aio的处理流程与 io uring和 Linux native aio存在差异,前者采用 submit→ wait→ reap模型,而后者则使用就地 submit→ poll。为了提升代码的对称性和美观性,决定将 posix aio的实现统一为 submit→ wait→ reap模型,这一改动不仅符合代码美学,还意外地解决了崩溃问题。在这一改动中,glibc在 aio_read后调用 aio_suspend,进而设置正确的 aio_error,使得系统能正确地处理并发 IO操作。

总结,经过深入分析和代码优化,发现 centos 7.3中的 glibc-2.17实现中,在 aio_read后并未立即设置 EINPROGRESS,而是在后续操作中设置。这种行为导致用户代码错误地认为 IO操作已完成,但实际上 IO未执行,随后使用了已释放的缓冲区,导致系统崩溃。在对代码进行优化后,通过在 aio_read后调用 aio_suspend设置正确的 aio_error,解决了这个问题,使得系统能正确地处理并发 IO操作。

进一步推测,在 glibc-2.17之后的某个版本中,该 bug被修复,使得在 aio_read后直接设置 EINPROGRESS,从而避免了调用 aio_suspend的必要性。这说明在后续版本中,glibc的 posix aio实现已经进行了优化,提高了系统的稳定性和可靠性。

Linux异步IO:AIO教程(libaio)

在Linux异步IO系统中,AIO(Asynchronous I/O)是一种用于非阻塞I/O操作的技术,通过异步操作提高系统IO性能。关键概念包括io_context_t,iocb(io callback),以及io_event。

io_context_t是异步I/O操作的上下文,包含一个完成队列,允许在线程间共享。创建上下文时,使用如下代码:

maxevents=最大事件数

io_context_t ctx= io_create(maxevents);

执行异步操作需要准备iocb(io callback),并使用io_submit函数提交请求。初始化iocb时,需要调用io_prep_pread或io_prep_pwrite函数。例如,为异步写入操作建立iocb时,使用如下代码:

io_prep_pwritev(iocb, file描述符,缓冲区起始位置,字节数,相对位置);

提交请求后,通过io_submit安排I/O操作。同时,可以通过设置O_DIRECT标志在原始块设备上进行操作,以避免缓存影响性能。

完成操作后,通过io_event处理结果。使用io_getevents函数从io_context_t获取完成事件,参数包括ctx_id(上下文ID),min_nr(最小事件数量),nr(最大事件数量),events(事件数组)以及timeout(等待时间)。返回值表示报告了多少完成事件,性能上,可以设置nr值稍大以减少调用次数。

在进行性能考虑时,AIO可以替代传统的同步I/O方法,例如使用select或poll。示例代码展示了如何使用Linux AIO实现异步文件读写操作,代码示例使用了谷歌的glog日志库、gflags命令行工具以及C++编码约定。编译时需要链接laio库。

参考资料:

github.com/littledan/li...

Linux异步IO

Linux中最常用的IO模型是同步IO,在这个模型中,当请求发出之后,应用程序就会阻塞,直到请求满足条件为止。这是一种很好的解决方案,调用应用程序在等待IO完成的时候不需要占用CPU,但是在很多场景中,IO请求可能需要和CPU消耗交叠,以充分利用CPU和IO提高吞吐率。

下图描绘了异步IO的时序,应用程序发起IO操作后,直接开始执行,并不等待IO结束,它要么过一段时间来查询之前的IO请求完成情况,要么IO请求完成了会自动被调用与IO完成绑定的回调函数。

Linux的AIO有多种实现,其中一种实现是在用户空间的glibc库中实现的,本质上是借用了多线程模型,用开启的新的线程以同步的方式做IO,新的AIO辅助线程与发起AIO的线程以pthread_cond_signal()的形式进行线程间的同步,glibc的AIO主要包含以下函数:

1、aio_read()

aio_read()函数请求对一个有效的文件描述符进行异步读操作。这个文件描述符可以代表一个文件、套接字,甚至管道,aio_read()函数原型如下:

aio_read()函数在请求进行排队之后就会立即返回(尽管读操作并未完成),如果执行成功就返回0,如果出现错误就返回-1。参数aiocb(AIO I/O Control Block)结构体包含了传输的所有信息,以及为AIO操作准备的用户空间缓冲区。在产生IO完成通知时,aiocb结构就被用来唯一标识所完成的IO操作。

2.aio_write()

aio_write()函数用来请求一个异步写操作。函数原型如下:

aio_write()函数会立即返回,并且它的请求以及被排队(成功时返回值为0,失败时返回值为-1)

3.aio_error()

aio_error()函数被用来确定请求的状态,其原型如下:

该函数的返回:

4.aio_return()

异步IO和同步阻塞IO方式之间有一个区别就是不能立即访问函数的返回状态,因为异步IO没有阻塞在read()调用上。在标准的同步阻塞read()调用中,返回状态是在该函数返回时提供的。

但是在异步IO中,我们要用aio_return()函数,原型如下:

只有在aio_error()调用确定请求已经完成(可能成功、也可能发生了错误)之后,才会调用这个函数,aio_return()的返回值就等价于同步情况中read()或者write系统调用的返回值。

5.aio_suspend()

用户可以用该函数阻塞调用进程,直到异步请求完成为止,调用者提供了一个aiocb引用列表,其中任何一个完成都会导致aio_suspend()返回。函数原型如下:

6.aio_cancel()

该函数允许用户取消对某个文件描述符执行的一个或所以IO请求。

要取消一个请求,用户需要提供文件描述符和aiocb指针,如果这个请求被成功取消了,那么这个函数就会返回AIO_CANCELED。如果请求完成了,就会返回AIO_NOTCANCELED。

7.lio_listio()

lio_listio()函数可用于同时发起多个传输。这个函数非常重要,它使得用户可以在一个系统调用中启动大量的IO操作,原型如下:

mode参数可以是LIO_WAIT或者是LIO_NOWAIT。LIO_WAIT会阻塞这个调用,直到所有的IO都返回为止,若是LIO_NOWAIT模型,在IO操作完成排队之后,该函数就会返回。list是一个aiocb的列表,最大元素的个数是由nent定义的。如果list的元素为null,lio_listio()会将其忽略。

阅读剩余
THE END