Действительно ли в 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 обычно выполняются с пулом рабочих потоков, не обязательно того же потока, что выдает ввод-вывод. эта модель несколько похожа на модель, предоставляемую boost:: asio, за исключением boost:: asio фактически не дает вам асинхронный блочный (дисковый) ввод-вывод
Отличие от epoll в Linux заключается в том, что на шаге 4 ни один ввод-вывод не имеет и все же случилось - он поднимает Шаг 1, чтобы прийти после шага 4, который является "обратным", если вы точно знаете, что вам уже нужно.
Запрограммировав большое количество встроенных, настольных и серверных операционных систем, я могу сказать, что эта модель асинхронного ввода-вывода очень естественна для некоторых видов программ. Это также очень высокая пропускная способность и низкие накладные расходы. Я думаю, что это один из оставшихся реальных недостатков модели ввода-вывода Linux на уровне API.
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
Все эти API должны быть переносимыми через BSD, Linux, MacOSX и даже Windows.
С точки зрения производительности я не видел никаких цифр, но подозреваю, что libuv может быть самым быстрым, из-за его легковесность?
Comments