Неблокирующий ввод/вывод был описан в разделах 5.9 и 44.9. Если поместить файловый дескриптор в неблокирующий режим, указав флаг состояния открытого файла O_NONBLOCK, то системные вызовы, которые не могут завершиться немедленно, не блокируются, а возвращают ошибку. Этот подход можно применять к именованным каналам, очередям FIFO, сокетам, терминалам, псевдотерминалам и ряду других видов устройств.
Неблокирующий ввод/вывод позволяет периодически проверять («опрашивать») возможность чтения или записи в файловый дескриптор. Например, можно сделать входящий файловый дескриптор неблокирующим и затем периодически выполнять неблокирующее чтение. При необходимости отслеживать несколько файловых дескрипторов можно пометить их все как неблокирующие и выполнять аналогичную проверку для каждого из них. Однако в данном случае активное чтение является нежелательным. Если выполнять его нечасто, то время реакции приложения на ввод/вывод может оказаться неприемлемо большим; с другой стороны, слишком частое циклическое чтение тратит впустую ресурсы процессора.
Термин «опрашивать» (англ. poll), который мы используем в этой главе, относится как к системному вызову poll(), предназначенному для мультиплексированного ввода/вывода, так и к процедуре «неблокирующей проверки состояния файлового дескриптора».
Если мы не хотим, чтобы процесс блокировался при выполнении ввода/вывода, то можем выделить специально для этого отдельный процесс. Пока родитель выполняет какие-то другие задачи, потомок блокируется, дожидаясь завершения ввода/вывода. При необходимости работать с несколькими файловыми дескрипторами можно создать по одному дочернему процессу для каждого из них. Проблема данного подхода заключается в ресурсоемкости и сложности. Создание и обслуживание процессов нагружает систему, а потомкам обычно приходится использовать некий механизм межпроцессного взаимодействия с целью информирования родителя о состоянии операций ввода/вывода.
Применение нескольких потоков вместо процессов является менее ресурсоемким, но все равно придется передавать между этими потоками информацию о состоянии операций ввода/вывода, что усложняет код, особенно если использовать пулы потоков для минимизации ресурсов, затрачиваемых на параллельное обслуживание большого количества клиентов. (Потоки могут быть особенно полезными в приложениях, которые вызывают сторонние библиотеки для выполнения блокирующего ввода/вывода; можно избежать блокировки главной программы, если обращаться к библиотеке в отдельном потоке.)
В связи с ограничениями, связанными с неблокирующим вводом/выводом и применением множественных потоков или процессов, зачастую имеет смысл использовать следующие альтернативные подходы.
•
• Программный интерфейс epoll поддерживается только в Linux версии 2.6 и выше. По аналогии с мультиплексированным вводом/выводом он позволяет следить за множеством файловых дескрипторов и проверять, допускают ли они чтение или запись. Как и ввод/вывод на основе сигналов, интерфейс epoll имеет значительно лучшую производительность при работе с большим количеством файловых дескрипторов.
В оставшейся части этой главы при обсуждении данных методик мы в основном будем применять отдельные процессы, хотя тот же подход уместен и в многопоточных приложениях.
Все три методики, описанные выше, направлены на достижение одного и того же результата: одновременного мониторинга одного или (чаще всего) нескольких файловых дескрипторов, чтобы проверить,