POSIX 异步 I/O (AIO)的状态是什么?

网络上散布着描述 POSIX AIO 设施的页面,其详细程度各不相同。这些都不是最近才发生的。不清楚他们到底在描述什么。例如,「官方」(?)支持 Linux 内核异步 I/O 的 Web 站点表示套接字不能工作,但是我的 Ubuntu 8.04.1工作站上的“ aio.h”手册页似乎都暗示它适用于任意的文件描述符。然后是 似乎在库层工作的另一个项目,它的文档更少。

我想知道:

  • POSIX AIO 的目的是什么?鉴于我能找到的最明显的实现例子是它不支持套接字,所以整个事情对我来说似乎很奇怪。是否只适用于异步磁盘 I/O?如果是这样,为什么要使用超通用 API?如果不是,为什么磁盘 I/O 是第一个受到攻击的东西?
  • 哪里有我可以查看的 完整 POSIX AIO 程序示例?
  • 真的有人用吗?
  • 哪些平台支持 POSIX AIO?他们支持哪部分?有人真的支持 <aio.h>似乎承诺的“对任何 FD 的任何 I/O”吗?

其他的多路复用机制对我来说非常好,但是随机的信息片段让我感到好奇。

26511 次浏览

Network I/O is not a priority for AIO because everyone writing POSIX network servers uses an event based, non-blocking approach. The old-style Java "billions of blocking threads" approach sucks horribly.

Disk write I/O is already buffered and disk read I/O can be prefetched into buffer using functions like posix_fadvise. That leaves direct, unbuffered disk I/O as the only useful purpose for AIO.

Direct, unbuffered I/O is only really useful for transactional databases, and those tend to write their own threads or processes to manage their disk I/O.

So, at the end that leaves POSIX AIO in the position of not serving any useful purpose. Don't use it.

Doing socket I/O efficiently has been solved with kqueue, epoll, IO completion ports and the likes. Doing asynchronous file I/O is sort of a late comer (apart from windows' overlapped I/O and solaris early support for posix AIO).

If you're looking for doing socket I/O, you're probably better off using one of the above mechanisms.

The main purpose of AIO is hence to solve the problem of asynchronous disk I/O. This is most likely why Mac OS X only supports AIO for regular files, and not sockets (since kqueue does that so much better anyway).

Write operations are typically cached by the kernel and flushed out at a later time. For instance when the read head of the drive happens to pass by the location where the block is to be written.

However, for read operations, if you want the kernel to prioritize and order your reads, AIO is really the only option. Here's why the kernal can (theoretically) do that better than any user level application:

  • The kernel sees all disk I/O, not just your applications disk jobs, and can order them at a global level
  • The kernel (may) know where the disk read head is, and can pick the read jobs you pass on to it in optimal order, to move the head the shortest distance
  • The kernel can take advantage of native command queuing to optimize your read operations further
  • You may be able to issue more read operations per system call using lio_listio() than with readv(), especially if your reads are not (logically) contiguous, saving a tiny bit of system call overhead.
  • Your program might be slightly simpler with AIO since you don't need an extra thread to block in a read or write call.

That said, posix AIO has a quite awkward interface, for instance:

  • The only efficient and well supported mean of event callbacks are via signals, which makes it hard to use in a library, since it means using signal numbers from the process-global signal namespace. If your OS doesn't support realtime signals, it also means you have to loop through all your outstanding requests to figure out which one actually finished (this is the case for Mac OS X for instance, not Linux). Catching signals in a multi-threaded environment also makes for some tricky restrictions. You can typically not react to the event inside the signal handler, but you have to raise a signal, write to a pipe or use signalfd() (on linux).
  • lio_suspend() has the same issues as select() does, it doesn't scale very well with the number of jobs.
  • lio_listio(), as implemented has fairly limited number of jobs you can pass in, and it's not trivial to find this limit in a portable way. You have to call sysconf(_SC_AIO_LISTIO_MAX), which may fail, in which case you can use the AIO_LISTIO_MAX define, which are not necessarily defined, but then you can use 2, which is defined as guaranteed to be supported.

As for real-world application using posix AIO, you could take a look at lighttpd (lighty), which also posted a performance measurement when introducing support.

Most posix platforms supports posix AIO by now (Linux, BSD, Solaris, AIX, tru64). Windows supports it via its overlapped file I/O. My understanding is that only Solaris, Windows and Linux truly supports async. file I/O all the way down to the driver, whereas the other OSes emulate the async. I/O with kernel threads. Linux being the exception, its posix AIO implementation in glibc emulates async operations with user level threads, whereas its native async I/O interface (io_submit() etc.) are truly asynchronous all the way down to the driver, assuming the driver supports it.

I believe it's fairly common among OSes to not support posix AIO for any fd, but restrict it to regular files.

A libtorrent developer provides a report on this: http://blog.libtorrent.org/2012/10/asynchronous-disk-io/

There is aio_write - implemented in glibc; first call of the aio_read or aio_write function spawns a number of user mode threads, aio_write or aio_read post requests to that thread, the thread does pread/pwrite and when it is finished the answer is posted back to the blocked calling thread.

Ther is also 'real' aio - supported by the kernel level (need libaio for that, see the io_submit call http://linux.die.net/man/2/io_submit ); also need O_DIRECT for that (also may not be supported by all file systems, but the major ones do support it)

see here:

http://lse.sourceforge.net/io/aio.html

http://linux.die.net/man/2/io_submit

Difference between POSIX AIO and libaio on Linux?