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



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



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



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



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



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



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



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




  1. выполните асинхронную операцию.

  2. свяжите асинхронную операцию с определенным портом завершения ввода-вывода.

  3. дождитесь завершения операций на этом порту

  4. когда ввод-вывод завершен, поток, ожидающий на порту, разблокируется и возвращает ссылку на ожидающую операцию ввода-вывода.


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



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



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

3 ответов:

Реальный ответ, на который косвенно указал Питер Тео, основан на io_setup() и io_submit(). В частности, функции" aio_", указанные Питером, являются частью эмуляции уровня пользователя 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().

Как описано в:

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,

И т. д.

На уровне пользовательского пространства, есть также io_submit() API-интерфейс и т. д (из 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) вас приветствует прямая реализация этих IO_*() API в ядре:

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 / дополнения / linux_altIO. pdf

Или, возможно:

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

"io_submit" и friends по-прежнему недоступны в glibc (см. справочные страницы io_submit), которые я проверил в своей 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

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

Для дискового ввода-вывода у нас есть posix aio, linux aio, sendfile и друзья.

Comments

    Ничего не найдено.