土法炼钢兴趣小组的算法知识备份

【操作系统百科】POSIX AIO 与 libaio

文章导航

分类入口
os
标签入口
#aio#libaio#async-io#io-submit#posix-aio

目录

异步 I/O 的理想:提交请求 → 不等待 → 完成时通知。Linux 有两套实现,都不完美。

一、先看图

flowchart TD
    subgraph POSIX_AIO["POSIX AIO(glibc)"]
        AIO_READ[aio_read] --> POOL[线程池<br/>clone/futex]
        POOL --> SYNC[同步 pread]
    end
    subgraph LIBAIO["libaio(内核)"]
        IO_SUBMIT[io_submit] --> CTX[io_context<br/>内核 AIO 环]
        CTX --> BIO_SUB[直接 bio 提交<br/>需 O_DIRECT]
        BIO_SUB --> IO_GET[io_getevents<br/>轮询完成]
    end
    subgraph IOURING["io_uring(替代)"]
        SQ[SQ ring] --> CQ[CQ ring<br/>无需 syscall]
    end

    classDef old fill:#f0883e22,stroke:#f0883e,color:#adbac7;
    classDef kern fill:#388bfd22,stroke:#388bfd,color:#adbac7;
    classDef new fill:#3fb95022,stroke:#3fb950,color:#adbac7;
    class AIO_READ,POOL,SYNC old
    class IO_SUBMIT,CTX,BIO_SUB,IO_GET kern
    class SQ,CQ new

二、POSIX AIO(glibc 实现)

2.1 API

struct aiocb cb = {
    .aio_fildes = fd,
    .aio_buf = buf,
    .aio_nbytes = 4096,
    .aio_offset = 0,
};
aio_read(&cb);
// ... 做其他工作 ...
aio_suspend(&cb_list, 1, NULL);  // 等待完成
ssize_t ret = aio_return(&cb);

2.2 真相

glibc 内部用 线程池 模拟异步——clone() 创建工作线程,每线程做同步 pread/pwrite

问题:

2.3 结论

不要在新代码中使用 POSIX AIO。它是 POSIX 标准的产物,Linux 实现是假异步。

三、libaio(内核 AIO)

3.1 API

io_context_t ctx = 0;
io_setup(128, &ctx);

struct iocb cb;
io_prep_pread(&cb, fd, buf, 4096, 0);

struct iocb *cbs[1] = {&cb};
io_submit(ctx, 1, cbs);

struct io_event events[1];
io_getevents(ctx, 1, 1, events, NULL);

io_destroy(ctx);

3.2 限制

3.3 使用场景

数据库引擎(MySQL InnoDB、RocksDB)在 O_DIRECT 场景下用 libaio。

四、libaio 的 eventfd 集成

io_set_eventfd(&cb, efd);  // 完成时 write(efd, 1)

可以把 eventfd 加入 epoll → 间接实现 epoll + AIO 组合。但还是需要 io_getevents 收割。

五、为什么 libaio 不支持 buffered I/O

buffered I/O 需要页缓存交互 → 可能触发 page fault → 内核不愿在 AIO 上下文处理阻塞操作 → 退化为同步。

io_uring 解决了这个问题:用独立的 io-wq 工作线程处理可能阻塞的请求。

六、io_cancel 的问题

int io_cancel(io_context_t ctx, struct iocb *iocb, struct io_event *result);

返回 -EINVAL 的概率极高——NVMe、SCSI 驱动大多不实现 cancel。已提交到设备的 I/O 无法取消。

七、性能对比

方案 4KB 随机读 IOPS syscall/IO
同步 pread ~100K 1
POSIX AIO ~80K 1 + 线程开销
libaio O_DIRECT ~300K ~0.5(批量)
io_uring ~400K+ ~0(SQPOLL)

libaio 在 O_DIRECT 批量场景有优势,但 io_uring 全面超越。

八、迁移建议

POSIX AIO    → 直接删除,用 io_uring 或线程池 + pread
libaio       → 新项目用 io_uring;老项目评估迁移成本
io_getevents → 可先加 eventfd 桥接,再逐步迁移

九、观察

# 查看进程的 AIO 上下文
cat /proc/<pid>/io    # rchar/wchar/syscr/syscw

# libaio ring buffer
cat /proc/<pid>/maps | grep aio

# 系统级
cat /proc/sys/fs/aio-max-nr   # 最大 AIO 上下文数
cat /proc/sys/fs/aio-nr        # 当前使用

十、小结


参考文献

工具


上一篇VFS I/O 路径 下一篇io_uring 内核内部

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-05-18 · os

【操作系统百科】io_uring 内核内部

io_uring 用共享内存 ring buffer 实现零 syscall 异步 I/O——SQ/CQ、SQPOLL、IOPOLL、注册 fd/buffer、multishot、安全模型演化。本文深入内核实现与工程实践。

2026-05-23 · os

【操作系统百科】异步 I/O 模型 benchmark

epoll、io_uring、libaio、阻塞线程池——四种异步模型的真实性能对比。本文用统一 workload 量化 echo server、静态文件服务、数据库 I/O 场景下的吞吐、延迟与 CPU 开销。

2025-08-20 · storage

【存储工程】Linux 异步 I/O:从 POSIX AIO 到 io_uring

在高吞吐存储系统中,同步 I/O 是性能的天花板。本文系统梳理 Linux 下三代异步 I/O 方案——POSIX AIO、Linux Native AIO(libaio)和 io_uring——的设计原理、编程接口、性能特征与工程实践,帮助你在实际项目中做出正确的技术选型。

2026-04-27 · os

【操作系统百科】内存回收

Linux 内存回收是 VM 最复杂的子系统之一。本文讲 active/inactive LRU、kswapd 与 direct reclaim、watermark 三线、swappiness 的真实含义、MGLRU 改造、memcg 回收与 PSI。


By .