发布网友
共1个回答
热心网友
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 实现已经进行了优化,提高了系统的稳定性和可靠性。