通过kqueue,epoll,IO完成端口等解决了有效执行套接字I / O的问题。进行异步文件I / O有点晚了(除了Windows的重叠I / O和solaris对posix AIO的早期支持)。
如果您要进行套接字I / O,最好使用上述机制之一。
因此,AIO的主要目的是解决异步磁盘I / O问题。这很可能是Mac OS X仅支持常规文件而不是套接字支持AIO的原因(因为kqueue还是要好得多)。
写操作通常由内核缓存,并在以后清除。例如,当驱动器的读取头正好经过要写入块的位置时。
但是,对于读取操作,如果您希望内核对读取进行优先级排序和排序,则AIO实际上是唯一的选择。这就是为什么内核可以(理论上)比任何用户级别的应用程序做得更好的原因:
- 内核可以查看所有磁盘I / O,而不仅仅是您的应用程序磁盘作业,并且可以在全局级别对其进行排序
- 内核(可能)知道磁盘读取头在哪里,并且可以以最佳顺序选择传递给它的读取作业,以将磁盘移动最短距离
- 内核可以利用本机命令排队来进一步优化您的读取操作
- 与使用readv()相比,使用lio_listio()可能比使用readv()发出更多的读取操作,尤其是如果您的读取不是(逻辑上)连续的,则可以节省少量的系统调用开销。
- 使用AIO,您的程序可能会稍微简单一些,因为您不需要额外的线程来阻塞读写调用。
就是说,posix AIO有一个非常尴尬的接口,例如:
- 事件回调的唯一有效且得到良好支持的手段是通过信号,这使得它很难在库中使用,因为这意味着使用来自进程全局信号名称空间的信号编号。如果您的操作系统不支持实时信号,则还意味着您必须遍历所有未完成的请求,以找出实际完成的请求(例如Mac OS X,而不是Linux)。在多线程环境中捕获信号也会带来一些棘手的限制。通常,您不能对信号处理程序中的事件做出反应,但是必须引发信号,写入管道或使用signalfd()(在Linux上)。
- lio_suspend()具有与select()相同的问题,但是随着作业数量的增加,它的伸缩性不是很好。
- lio_listio(),由于已实现,因此您可以传递的工作数量相当有限,并且以可移植的方式找到此限制并非易事。您必须调用sysconf(_SC_AIO_LISTIO_MAX),这可能会失败,在这种情况下,您可以使用不一定要定义的AIO_LISTIO_MAX定义,但是您可以使用2(已定义为保证受支持)。
至于使用posix AIO的实际应用程序,您可以看一看lighttpd(lighty),它还发布了性能评估在引入支持时。
到目前为止,大多数posix平台都支持posix AIO(Linux,BSD,Solaris,AIX,tru64)。Windows通过其重叠的文件I / O支持它。我的理解是,只有Solaris,Windows和Linux才真正支持异步。文件I / O一直到驱动程序,而其他OS则模拟异步。具有内核线程的I / O。Linux是个例外,它在glibc中的posix AIO实现模拟了用户级线程的异步操作,而其本机异步I / O接口(io_submit()等)一直到驱动程序都是真正异步的,前提是驱动程序支持它。
我相信在OS中,不为任何fd支持posix AIO而是将其限制为常规文件是相当普遍的。