В статье "Understanding Network I/O" (часть 1, часть 2) приводятся примеры создания сетевых программ на языке Python, включая использования мультитредовой модели и опроса состояний через select/ poll.
Материал подтолкнул меня обобщить используемые модели ввода/вывода:
Стратегии организации ввода-вывода:
- Блокируемый I/O - после вызова read/write происходит блокировка до завершения
операции, функция завершается только после принятия или передачи блока данных.
- Неблокируемый I/0 - функция завершается сразу, если данные не были приняты/отправлены
возвращается код ошибки (т.е. нужно вызывать функции I/O в цикле пока не получим
положительный результат).
- Мультиплексирование через select/poll - опрашиваем список состояния сокетов,
перебирая состояния определяем сокеты готовые для приема/передачи.
Главный минус - затраты на перебор, особенно при большом числе неактивных
сокетов.
- select - число контролируемых сокетов ограничено лимитом FD_SETSIZE,
в некоторых случаях лимит обходится пересборкой программы, в других - пересборкой
ядра ОС.
- poll - нет лимита FD_SETSIZE, но менее эффективен из за большего размера передаваемой
в ядро структуры.
- Генерация сигнала SIGIO при изменении состояния сокета (ошибка, есть данные для приема,
или отправка завершена), который обрабатывает обработчик SIGIO.
В классическом виде применение ограничено и трудоемко, подходит больше для UDP.
- Асинхронный I/O - описан в POSIX 1003.1b (aio_open, aio_write, aio_read...),
функция aio_* завершается мгновенно, далее процесс сигнализируется о
полном завершении операции ввода/вывода (в предыдущих пунктах процесс информировался
о готовности прочитать или передать данные, т.е. данные еще нужно было принять или отправить
через read/write, в aio_* процесс сигнализируется когда данные полностью получены и скопированы в локальный буфер).
- Передача данных об изменении состояния сокета через генерацию событий. (специфичные
для определенных ОС решения, малопереносимы, но эффективны).
- kqueue - лучшее для FreeBSD, NetBSD. Данные о нескольких событиях могут быть переданы за раз, очень гибкое решение.
- /dev/epoll - лучшее для 2.6 Linux ядра, передача нескольких событий за раз, трудоемкость поддержки /dev/epoll если параллельно в программе поддерживаются другие механизмы нотификации.
- Realtime Signals (F_SETSIG) - лучшее для 2.4 Linux ядра.
- /dev/poll - имеет смысл в Solaris, в Linux реализация недостаточно хороша.
- Ссылки:
|