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

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

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

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

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


person Glyph    schedule 17.09.2008    source источник


Ответы (4)


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

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

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

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

person Zan Lynx    schedule 17.09.2008
comment
А как насчет чтения / записи из сетевых (NFS, Samba) файловых систем? - person Alex B; 27.12.2009
comment
Что ж. У меня есть несколько больших тупых писателей, которые, если я позволю им перейти в кеш, будут на пике использовать dirty_ratio, блокируя всех остальных. Если я просто использую для них прямой ввод-вывод, это будет слишком медленно. Если бы у меня был только один поток, я мог бы управлять самостоятельно, но было бы сложно поддерживать разные приоритеты ввода-вывода в одном потоке. AIO + CFQ действительно кажутся хорошей комбинацией, если бы AIO работал - person n-alexander; 23.08.2010
comment
Я не согласен. Дисковый ввод-вывод обычно буферизуется, но может блокировать. Когда poll () обрабатывает файловый FD, он всегда сообщает, что FD доступен для чтения, даже если он будет заблокирован. Это делает невозможным выполнение неблокирующих операций с файлами на диске равномерно, если только не используются потоки или AIO. - person Hongli; 10.10.2010
comment
@Hongli: Механизмы БД, которые я видел, используют собственные потоки или процессы. У вас есть пример механизма SQL, который использует AIO? - person Zan Lynx; 11.10.2010
comment
Нет такой вещи, как асинхронные сокеты. Не в том смысле, что вы можете отправить кучу операций записи в один сокет, поскольку порядок важен. Для тех протоколов, где порядок не важен, звонки не блокируются ... - person Matt Joiner; 15.11.2010
comment
@Matt: порядок не важен для сокетов дейтаграмм. @Zan: асинхронный ввод-вывод очень удобен для предварительной буферизации потоковых данных в реальном времени, например медиаплееры. - person Ben Voigt; 15.03.2011
comment
AIO не имеет ничего общего с миллиардами блокирующих потоков. Вся суть AIO в том, чтобы иметь фиксированное количество потоков службы ввода-вывода. - person Jon Watte; 19.11.2012
comment
@JonWatte: Мой ответ не имел ничего общего с миллиардами потоков. Он объяснял, что сетевые приложения уже используют систему на основе событий, в которой AIO бесполезен. - person Zan Lynx; 19.11.2012
comment
Неправда, что AIO бесполезен в системах, основанных на событиях. Фактически вы можете получить сеть с нулевым копированием с помощью правильного AIO, чего нельзя добиться с помощью уведомления на основе событий для recv (). Другие вещи могут сговориться, чтобы сделать это в основном теоретическим ограничением, но я думаю, что отсутствие надлежащего AIO (а-ля OVERLAPPED в Windows) - одна из последних больших дыр в Linux. - person Jon Watte; 20.11.2012
comment
Согласно Теду Ц'о в 2010 году fadvise НЕ гарантированно будет асинхронным: lkml.indiana.edu/hypermail/linux/kernel/1012.0/01942.html Если это все еще так, то AIO все еще имеет свое место. - person Chris Pacejo; 22.11.2013
comment
В ответе ничего не говорится о том, какие платформы на самом деле поддерживают какие операции с какими дескрипторами. - person EFraim; 23.08.2017

Эффективное выполнение ввода-вывода сокетов было решено с помощью 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, а ограничивать его обычными файлами.

person Arvid    schedule 15.03.2011
comment
С момента появления Win32 в Windows была реализована поддержка файлового ввода / вывода с ПЕРЕЗАГРУЗКОЙ. Это совсем не ново. А в POSIX пространство имен сигналов не является глобальным для процесса, а для каждого потока. Сигналы доставляются в определенные потоки (или aio - исключение, точно не помню?). - person Ben Voigt; 15.03.2011
comment
Невозможно указать, в какой поток AIO отправляет свои сигналы. В linux кажется, что в основном доставляется в поток, который запустил команду aio _ * (), но не всегда (единственное решение, которое я нашел для этого, - создать несколько signalfds). Несколько лет назад в список рассылки ядра был добавлен патч для Linux, который добавлял его, но он так и не попал, и это было бы расширением POSIX. В Mac OS X сигналы в основном доставляются в основной поток (по моему опыту). Я не думаю, что POSIX требует определенного поведения, если это так, я бы хотел увидеть часть спецификации. - person Arvid; 15.03.2011
comment
Реализация aio_read / write в glibc использует потоки в пользовательской среде, поэтому здесь не используются даже потоки ядра. - person Marenz; 14.04.2011
comment
Что обычно означает «всегда»? Записи кешируются ядром при любом методе, или при использовании AIO? Похоже, должен быть способ, чтобы программное обеспечение было уверено, что запись была успешно завершена; в противном случае цели целостности и транзакции не могут быть достигнуты. - person MikeB; 09.06.2013
comment
Еще один живой пример, в котором вы можете использовать AIO, - это nginx. Поддерживаются все режимы. Если вы предпочитаете разгрузку в потоки пользовательской среды, вы обычно обнаружите, что это намного хуже, чем прямой ввод-вывод, но собственный AIO Linux находится на одном уровне с прямым вводом-выводом. Ситуация, когда AIO может быть существенно выгодным, - это сильная нагрузка на кеш страницы. Концептуальную разницу между Async и Direct IO можно увидеть здесь ftp.dei.uc.pt/pub/linux/kernel/people/suparna/aio-linux.pdf - person wick; 05.08.2015
comment
@Arvid The only efficient and well supported mean of event callbacks are via signals все еще правда сегодня? Я читаю posix aio и обнаружил, что aio_return легко получить статус. Вы можете объяснить? Благодарность - person gacopu; 23.05.2019
comment
конечно, вы всегда должны вызывать aio_return() после завершения iocb. Я имел в виду, что меня уведомили о его завершении. Вы можете опросить aio_return, но он не будет хорошо масштабироваться. В последнее время я особо не смотрел на состояние aio, поэтому не знаю, сильно ли оно улучшилось. В первую очередь MacOS имеет плохую поддержку, где сигналы - ваш единственный вариант, без каких-либо намеков на то, какой iocb завершен. - person Arvid; 23.05.2019
comment
@Arvid Большое спасибо. Кстати, а как libtorrent сейчас выполняет ввод-вывод (linux)? Linux родной AIO? нормальный pwrite / pread? posix AIO? - person gacopu; 25.05.2019
comment
в стабильном выпуске есть пул потоков с preadv() и pwritev() вызовами. master имеет пул потоков с чтением и записью файлов с отображением памяти. Учитывая, куда идет хранилище, я думаю, что будущее за вводом-выводом с отображением памяти. Однако он синхронный, поэтому вам все равно нужен пул потоков. - person Arvid; 26.05.2019

Разработчик libtorrent предоставляет отчет об этом: http://blog.libtorrent.org/2012/10/asynchronous-disk-io/

person Allen    schedule 26.10.2012
comment
Это будет Арвид, который тоже ответил выше :) - person dpn; 30.12.2012
comment
Отличный отчет. Очень поучительно. Спасибо, что поделились. - person chmike; 11.05.2016

Есть 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?

person MichaelMoser    schedule 27.10.2014
comment
Многие недостатки aio_write описаны выше в stackoverflow.com/a/5307557/13564. - person Glyph; 28.10.2014