Подтвердить что ты не робот

Действительно ли нет асинхронного ввода-вывода блоков в Linux?

Рассмотрим приложение, которое связано с ЦП, но также имеет высокопроизводительные требования ввода-вывода.

Я сравниваю Linux файл ввода/вывода с Windows, и я не вижу, как epoll поможет программе Linux вообще. Ядро скажет мне, что дескриптор файла "готов к чтению", но мне все равно нужно вызвать функцию блокировки read(), чтобы получить мои данные, и если я хочу читать мегабайты, довольно ясно, что это будет заблокировано.

В Windows я могу создать дескриптор файла с набором OVERLAPPED, а затем использовать неблокирующий ввод-вывод и получить уведомление о завершении ввода-вывода и использовать данные из этой функции завершения. Мне нужно потратить время на настенные часы на уровне приложения, ожидая данных, а это значит, что я могу точно настроить количество потоков на мое количество ядер и получить 100% эффективное использование процессора.

Если мне нужно эмулировать асинхронный ввод-вывод в Linux, тогда мне нужно выделить некоторое количество потоков для этого, и эти потоки потратят немного времени на выполнение CPU, и много времени блокирует I/O, плюс в обмене сообщениями на/из этих потоков будут накладные расходы. Таким образом, я либо переподписываю, либо недоиспользую свои ядра процессора.

Я посмотрел на mmap() + madvise() (WILLNEED) как "асинхронный ввод-вывод" плохой человек ", но он все еще не проходит весь путь, потому что я не могу получить уведомление, когда это будет сделано - - Я должен" угадать ", и если я думаю" неправильно", я закончу блокирование доступа к памяти, ожидая, что данные будут поступать с диска.

Linux, похоже, запускает асинхронные операции ввода-вывода в io_submit, и, похоже, также имеет реализацию POSIX aio для пользовательского пространства, но на некоторое время это было так, и я не знаю никого, кто ручался бы за эти системы для критически важных высокопроизводительных приложений.

Модель Windows работает примерно так:

  • Выполните асинхронную операцию.
  • Свяжите асинхронную операцию с конкретным портом завершения ввода-вывода.
  • Дождитесь завершения операций над этим портом
  • Когда ввод-вывод завершен, поток, ожидающий от порта, блокируется и возвращает ссылку на ожидающую операцию ввода-вывода.

Шаги 1/2 обычно выполняются как одна вещь. Шаги 3/4 обычно выполняются с пулом рабочих потоков, а не (обязательно) тот же поток, что и проблемы ввода-вывода. Эта модель несколько похожа на модель, предоставляемую boost:: asio, за исключением boost:: asio фактически не дает вам асинхронного ввода-вывода на основе блоков (диска).

Разница с epoll в Linux заключается в том, что на шаге 4 никаких операций ввода-вывода еще не произошло - он поднимает шаг 1 после шага 4, который является "обратным", если вы точно знаете, что вам нужно.

Запросив большое количество встроенных, настольных и серверных операционных систем, я могу сказать, что эта модель асинхронного ввода-вывода очень полезна для определенных видов программ. Это также очень высокая пропускная способность и низкий расход. Я думаю, что это один из оставшихся реальных недостатков модели ввода-вывода Linux на уровне API.

4b9b3361

Ответ 1

Реальный ответ, на который косвенно указал Питер Тео, основан на io_setup() и io_submit(). В частности, функции "aio_", обозначенные Peter, являются частью эмуляции уровня пользователя glibc на основе потоков, что не является эффективной реализацией. Реальный ответ:

io_submit(2)
io_setup(2)
io_cancel(2)
io_destroy(2)
io_getevents(2)

Обратите внимание, что в man-странице, датированной 2012-08, говорится, что эта реализация еще не созрела до такой степени, что она может заменить эмуляцию пользовательского пространства glibc:

http://man7.org/linux/man-pages/man7/aio.7.html

эта реализация еще не созрела до такой степени, что POSIX Реализация AIO может быть полностью реализована с использованием ядра системных вызовов.

Итак, согласно последней документации ядра, которую я могу найти, Linux еще не имеет зрелой, асинхронной модели ввода-вывода на основе ядра. И, если я предполагаю, что документированная модель на самом деле зрелая, она по-прежнему не поддерживает частичный ввод-вывод в смысле recv() vs read().

Ответ 2

Как объясняется в:

http://code.google.com/p/kernel/wiki/AIOUserGuide

и здесь:

http://www.ibm.com/developerworks/library/l-async/

Linux предоставляет асинхронные операции ввода-вывода на уровне ядра, API следующим образом:

aio_read    Request an asynchronous read operation
aio_error   Check the status of an asynchronous request
aio_return  Get the return status of a completed asynchronous request
aio_write   Request an asynchronous operation
aio_suspend Suspend the calling process until one or more asynchronous requests have completed (or failed)
aio_cancel  Cancel an asynchronous I/O request
lio_listio  Initiate a list of I/O operations

И если вы спросите, кто является пользователем этого API, это ядро ​​- здесь показано только небольшое подмножество:

./drivers/net/tun.c (for network tunnelling):
static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv,

./drivers/usb/gadget/inode.c:
ep_aio_read(struct kiocb *iocb, const struct iovec *iov,

./net/socket.c (general socket programming):
static ssize_t sock_aio_read(struct kiocb *iocb, const struct iovec *iov,

./mm/filemap.c (mmap of files):
generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov,

./mm/shmem.c:
static ssize_t shmem_file_aio_read(struct kiocb *iocb,

и др.

На уровне пользовательского пространства существует также API io_submit() и др. (от glibc), но в следующей статье предлагается альтернатива использованию glibc:

http://www.fsl.cs.sunysb.edu/~vass/linux-aio.txt

Он непосредственно реализует API для таких функций, как io_setup(), как прямой syscall (минуя зависимости glibc), должно существовать сопоставление ядра через ту же самую подпись "__NR_io_setup". При поиске исходного кода ядра:

http://lxr.free-electrons.com/source/include/linux/syscalls.h#L474 (URL-адрес применим для последней версии 3.13), вас приветствует прямая реализация этих API io _ *() в ядре:

474 asmlinkage long sys_io_setup(unsigned nr_reqs, aio_context_t __user *ctx);
475 asmlinkage long sys_io_destroy(aio_context_t ctx);
476 asmlinkage long sys_io_getevents(aio_context_t ctx_id,
481 asmlinkage long sys_io_submit(aio_context_t, long,
483 asmlinkage long sys_io_cancel(aio_context_t ctx_id, struct iocb __user *iocb,

Более поздняя версия glibc должна сделать это использование "syscall()" для вызова sys_io_setup() ненужным, но без последней версии glibc вы всегда можете сделать этот вызов самостоятельно, если вы используете более поздний ядро ​​с этими возможностями из "sys_io_setup()".

Конечно, есть и другие параметры пользовательского пространства для асинхронного ввода-вывода (например, используя сигналы?):

http://personal.denison.edu/~bressoud/cs375-s13/supplements/linux_altIO.pdf

или perhap:

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

"io_submit" и друзья по-прежнему недоступны в glibc (см. io_submit manpages), которые я проверил в своем Ubuntu 14.04, но этот API специфичен для Linux.

Другие, такие как libuv, libev и libevent, также являются асинхронными API:

http://nikhilm.github.io/uvbook/filesystem.html#reading-writing-files

http://software.schmorp.de/pkg/libev.html

http://libevent.org/

Все эти API должны быть переносимыми через BSD, Linux, MacOSX и даже Windows.

С точки зрения производительности я не видел никаких цифр, но подозрение на libuv может быть самым быстрым из-за его легковесности?

https://ghc.haskell.org/trac/ghc/ticket/8400

Ответ 3

(2019) Если вы используете ядро 5.1 или выше, вы можете использовать интерфейс io_uring для файлового ввода-вывода и получить отличную асинхронную работу.

По сравнению с существующим интерфейсом libaio/KAIO io_uring имеет следующие преимущества:

  • Работает с буферизованным и прямым вводом/выводом
  • Проще в использовании
  • При желании может работать в режиме опроса
  • Меньше накладных расходов на бухгалтерию на ввод/вывод
  • Снижение нагрузки на процессор из-за меньшего количества переключений контекста системного вызова в пользовательском пространстве/ядре (в наши дни это очень важно из-за влияния смягчения последствий/расплавления)
  • "Связанный режим", который можно использовать для выражения зависимостей между группами ввода-вывода (> = 5,3 ядра)
  • Не блокируется каждый раз, когда звезды не идеально выровнены

По сравнению с glibc POSIX aio, io_uring имеет следующие преимущества:

В документе "Эффективный ввод-вывод с io_uring" подробно рассматриваются преимущества и использование io_uring. Есть также видео-презентация "Faster IO through io_uring" автора io_uring Дженса Аксбо.

"Поддержка частичного ввода-вывода в смысле recv() vs read()": в ядро 5.3 вошел патч , который автоматически повторяет короткие чтения io_uring. Еще не исправленный патч (который, как я предполагаю, появится в ядре 5.4) еще больше подправляет поведение, и автоматически выполняет только короткие чтения при работе с "обычными" файлами, когда запрос не REQ_F_NOWAIT (похоже, вы можете запросить REQ_F_NOWAIT через IOCB_NOWAIT или открыв файл с помощью O_NONBLOCK). Таким образом, похоже, что вы можете получить recv() style- "короткое" поведение ввода/вывода также из io_uring.

Очевидно, что на момент написания интерфейса io_uring был очень новым, но, надеюсь, он откроет лучшую историю асинхронного ввода-вывода на основе файлов для Linux.

Ответ 4

Для ввода/вывода сетевого сокета, когда он "готов", он не блокируется. То, что означает O_NONBLOCK и "ready".

Для диска i/o мы имеем posix aio, linux aio, sendfile и друзей.