我将使用 aio 进行异步读取。当 aio 完成并触发信号处理程序时,我可能需要进行另一个 aio_read 调用并继续。
安全函数中没有提到 aio_read(在 man 信号中)。不过,普通阅读是。
在 aio 信号处理程序中执行后续 aio_read 调用有什么危险?
作为提议的可以使用 POSIX AIO 的 Boost.AFIO 的作者,我强烈建议不要使用 POSIX AIO。我并不孤单,@arvid 同样反对:http ://blog.libtorrent.org/2012/10/asynchronous-disk-io/ 。API 本身的设计很糟糕,因此负载扩展性很差,除非您使用特定于操作系统的替代方案或 AIO 扩展,例如 BSD kqueue。POSIX AIO 本质上是无用的。
此外,AIO 调用在您可能正在使用的 Linux 上不是信号安全的。这是因为在 Linux 上,它们是使用基于线程池的仿真在用户空间中实现的。在 BSD 上,AIO 调用具有适当的内核系统调用接口,但在内核中变成 - 是的,你猜对了 - 除非打开 O_DIRECT,否则基于线程池的仿真。
因此,除非您的所有 i/o 都启用了 O_DIRECT,否则您在 POSIX 上总是使用线程池要好得多。如果 O_DIRECT 确实总是打开,Linux 提供了一个自定义内核 API,详细信息在http://man7.org/linux/man-pages/man2/io_submit.2.html相当有效,如果您替换信号驱动处理,则在 BSD 上使用 BSD kqueues(https://www.freebsd.org/cgi/man.cgi?kqueue,请参阅 EVFILT_AIO )然后使用 O_DIRECT 东西也可以很好地扩展,无论如何都比线程池更好。
在任何 POSIX 平台上使用基于信号的完成处理具有可怕的性能。AFIO v2 提供了一个通用的 POSIX AIO 后端,它是可怕的、可怕的、可怕的。避免像瘟疫一样。
请注意,线程池同步 API 设计是可移植的,适用于大多数用例,并且是我(实际上是 arvid)向任何没有高度专业化需求的人推荐的设计,例如编写需要对物理存储层进行非常严格控制的数据库后端, 除了 O_DIRECT|O_SYNC 之外的任何东西都不是一个选项。
好吧,说了这么多,如果你真的想使用信号驱动的 aio,我认为这是因为你想将你的文件 i/o 与非文件 i/o 的东西多路复用,因此你不能使用 aio_suspend() 哪个是执行此操作的正确 API。AFIO v2 处理此问题的方式是使用实时信号来中断 aio_suspend(),当需要处理与 aio 无关的内容时,可以对其进行处理并重新启动 aio_suspend()。在处理竞争和死锁时需要非常小心,并且需要小心地屏蔽和取消屏蔽调用 aio_suspend() 的线程的信号,以免实时信号丢失而导致丢失唤醒。总而言之,通过线程池 + 同步 API 获得的通常低得多的 i/o 性能是不值得的。