Каков статус асинхронного ввода-вывода POSIX (AIO)?


93

В сети разбросаны страницы, которые описывают возможности POSIX AIO с разной степенью детализации. Ни один из них не совсем недавний. Непонятно, что именно они описывают. Например, на «официальном» (?) Веб-сайте поддержки асинхронного ввода-вывода ядра Linux говорится, что сокеты не работают, но все страницы руководства «aio.h» на моей рабочей станции Ubuntu 8.04.1, похоже, подразумевают, что он работает для произвольных файловых дескрипторов. Затем есть еще один проект, который, похоже, работает на уровне библиотеки с еще меньшим количеством документации.

Я хотел бы знать:

  • Какова цель POSIX AIO? Учитывая, что наиболее очевидный пример реализации, которую я могу найти, говорит, что она не поддерживает сокеты, мне все это кажется странным. Это только для асинхронного дискового ввода-вывода? Если да, то почему гипер-общий API? Если нет, то почему дисковый ввод-вывод первым подвергается атаке?
  • Где я могу посмотреть примеры полных программ POSIX AIO?
  • Кто-нибудь действительно этим пользуется?
  • Какие платформы поддерживают POSIX AIO? Какие части они поддерживают? Кто-нибудь действительно поддерживает подразумеваемое <aio.h>обещание «Любой ввод-вывод для любого FD» ?

Другие доступные мне механизмы мультиплексирования совершенно хороши, но случайные фрагменты информации, плавающие вокруг, вызвали у меня любопытство.

Ответы:


27

Сетевой ввод-вывод не является приоритетом для AIO, потому что каждый, кто пишет сетевые серверы POSIX, использует неблокирующий подход, основанный на событиях. Подход Java в старом стиле «миллиарды блокирующих потоков» ужасно отстой.

Операции ввода-вывода для записи на диск уже буферизованы, а операции ввода-вывода для чтения с диска могут быть предварительно загружены в буфер с помощью таких функций, как posix_fadvise. Это оставляет прямой небуферизованный дисковый ввод-вывод в качестве единственной полезной цели для AIO.

Прямой небуферизованный ввод-вывод действительно полезен только для транзакционных баз данных, и они склонны писать свои собственные потоки или процессы для управления вводом-выводом на диске.

Итак, в итоге POSIX AIO оказывается в положении, не служащем какой-либо полезной цели. Не используйте это.


8
Как насчет чтения / записи из сетевых файловых систем (NFS, Samba)?
Alex B

1
Что ж. У меня есть несколько больших тупых писателей, которые, если я позволю им перейти в кеш, будут на пике использовать dirty_ratio, блокируя всех остальных. Если я просто использую для них прямой ввод-вывод, это будет слишком медленно. Если бы у меня был только один поток, я мог бы управлять самостоятельно, но было бы сложно поддерживать разные приоритеты ввода-вывода в одном потоке. AIO + CFQ действительно кажутся хорошей комбинацией, если бы AIO работал
н-александр

37
Я не согласен. Дисковый ввод / вывод обычно буферизируется, но может блокировать. Когда poll () обрабатывает файл FD, он всегда сообщает, что FD доступен для чтения, даже если он блокируется. Это делает невозможным выполнение неблокирующих операций с дисковыми файлами равномерно, если только не используются потоки или AIO.
Хунли

2
@Matt: порядок не важен для сокетов дейтаграмм. @Zan: асинхронный ввод-вывод очень удобен для предварительной буферизации потоковых данных в реальном времени, например, для медиаплееров.
Ben Voigt

13
Неправда, что AIO бесполезен в системах, основанных на событиях. Вы действительно можете получить сеть с нулевым копированием с помощью правильного AIO, чего нельзя добиться с помощью уведомления на основе событий для recv (). Другие вещи могут сговориться, чтобы сделать это в основном теоретическим ограничением, но я думаю, что отсутствие надлежащего AIO (а-ля OVERLAPPED в Windows) - одна из последних больших дыр в Linux.
Jon Watte

70

Эффективное выполнение ввода-вывода сокетов решено с помощью kqueue, epoll, портов завершения ввода-вывода и т.п. Выполнение асинхронного файлового ввода-вывода - это своего рода поздний этап (помимо перекрывающегося ввода-вывода Windows и ранней поддержки posix AIO в Solaris).

Если вы ищете возможность ввода-вывода через сокеты, вам, вероятно, лучше использовать один из вышеперечисленных механизмов.

Таким образом, основная цель AIO - решить проблему асинхронного дискового ввода-вывода. Скорее всего, поэтому Mac OS X поддерживает только AIO для обычных файлов, а не сокетов (поскольку kqueue в любом случае делает это намного лучше).

Операции записи обычно кэшируются ядром и удаляются позже. Например, когда считывающая головка привода проходит мимо места, где должен быть записан блок.

Однако для операций чтения, если вы хотите, чтобы ядро ​​расставляло приоритеты и упорядочивало чтение, AIO - действительно единственный вариант. Вот почему ядро ​​может (теоретически) делать это лучше, чем любое приложение пользовательского уровня:

  • Ядро видит все дисковые операции ввода-вывода, а не только дисковые задания ваших приложений, и может упорядочить их на глобальном уровне.
  • Ядро (может) знать, где находится считывающая головка диска, и может выбирать задания чтения, которые вы передаете ему в оптимальном порядке, чтобы переместить головку на кратчайшее расстояние.
  • Ядро может использовать встроенную очередь команд для дальнейшей оптимизации операций чтения.
  • Вы можете выполнять больше операций чтения для одного системного вызова с помощью 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 поддерживает его через перекрывающийся файловый ввод-вывод. Насколько я понимаю, только Solaris, Windows и Linux действительно поддерживают асинхронность. файловый ввод-вывод вплоть до драйвера, тогда как другие ОС эмулируют асинхронный режим. Ввод-вывод с потоками ядра. Linux является исключением, его реализация posix AIO в glibc эмулирует асинхронные операции с потоками пользовательского уровня, тогда как его собственный интерфейс асинхронного ввода-вывода (io_submit () и т. Д.) Действительно асинхронен на всем пути вплоть до драйвера, если драйвер поддерживает его. .

Я считаю, что среди операционных систем довольно распространено не поддерживать posix AIO для любого fd, а ограничивать его обычными файлами.


С момента появления Win32 в Windows была реализована поддержка файлового диска для операций ввода-вывода с перекрытием. Это совсем не ново. А в POSIX пространство имен сигналов не является глобальным для процесса, а для каждого потока. Сигналы доставляются в определенные потоки (или aio - исключение, точно не помню?).
Ben Voigt

1
Невозможно указать, какой поток AIO передает свои сигналы. В Linux кажется, что он в основном доставляет его в поток, который запустил команду aio _ * (), но не всегда (единственное решение, которое я нашел для этого, - создать несколько signalfds). Несколько лет назад в список рассылки ядра был добавлен патч для Linux, который добавлял его, но он так и не попал, и это было бы расширением POSIX. В Mac OS X сигналы в основном доставляются в основной поток (по моему опыту). Я не думаю, что POSIX требует определенного поведения, если это так, мне бы хотелось увидеть часть спецификации.
Arvid

5
Реализация aio_read / write в glibc использует потоки в пользовательской среде, поэтому здесь не используются даже потоки ядра.
Marenz

Что означает «всегда обычно»? Записи кешируются ядром при любом методе или при использовании AIO? Похоже, должен быть способ, чтобы программное обеспечение было уверено, что запись была успешно завершена; в противном случае цели целостности и транзакции не могут быть достигнуты.
MikeB 08

Еще один живой пример, в котором вы можете использовать AIO, - это nginx. Поддерживаются все режимы. Если вы предпочитаете разгрузку в потоки пользовательского уровня, вы обычно обнаружите, что это намного хуже, чем прямой ввод-вывод, но собственный AIO Linux находится на одном уровне с прямым вводом-выводом. Ситуация, когда AIO может быть существенно выгодным, - это серьезное давление на кеш страницы. Концептуальное различие между Async и прямой ИО можно увидеть здесь ftp.dei.uc.pt/pub/linux/kernel/people/suparna/aio-linux.pdf
фитиль


2

Есть aio_write - реализовано в glibc; Первый вызов функции aio_read или aio_write порождает ряд потоков пользовательского режима, запросы сообщений aio_write или aio_read к этому потоку, поток выполняет pread / pwrite, и когда он завершается, ответ отправляется обратно в заблокированный вызывающий поток.

Это также «настоящий» aio - поддерживается на уровне ядра (для этого требуется libaio, см. Вызов io_submit http://linux.die.net/man/2/io_submit ); также нужен для этого O_DIRECT (также может поддерживаться не всеми файловыми системами, но основные поддерживают его)

глянь сюда:

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

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

Разница между POSIX AIO и libaio в Linux?


Многие из недостатков aio_writeописаны выше, в stackoverflow.com/a/5307557/13564
Glyph
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.