UNIX неблокирующий ввод / вывод: O_NONBLOCK против FIONBIO - PullRequest
87 голосов
/ 19 июля 2009

В каждом примере и обсуждении, с которыми я сталкиваюсь в контексте программирования сокетов BSD, кажется, что рекомендуемый способ установить дескриптор файла на неблокирующий режим ввода / вывода - использовать флаг O_NONBLOCK в fcntl(), например

int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);

Я занимаюсь сетевым программированием в UNIX более десяти лет и всегда использовал для этого вызов FIONBIO ioctl():

int opt = 1;
ioctl(fd, FIONBIO, &opt);

Никогда не задумывался, почему. Просто научился так.

Есть ли у кого-нибудь комментарии относительно возможных соответствующих достоинств того или другого? Я предполагаю, что локус переносимости несколько отличается, но я не знаю, в какой степени ioctl_list(2) не говорит об этом аспекте отдельных ioctl методов.

Ответы [ 3 ]

126 голосов
/ 20 июля 2009

До стандартизации было ioctl( ... FIONBIO ... ) и fcntl( ... O_NDELAY ... ), но они вели себя непоследовательно между системами и даже внутри та же система. Например, FIONBIO обычно работает с сокетами, а O_NDELAY - с ttys, что приводит к большим несоответствиям для таких вещей, как трубы, fifos и устройства. И если вы не знаете, какой у вас дескриптор файла, вам придется установить оба, чтобы быть уверенным. Но кроме того, неблокирующее чтение без доступных данных также было указано непоследовательно; в зависимости от ОС и типа дескриптора файла, чтение может вернуть 0, или -1 с ошибкой EAGAIN, или -1 с ошибкой EWOULDBLOCK. Даже сегодня установка FIONBIO или O_NDELAY в Solaris приводит к тому, что чтение без данных возвращает 0 для tty или канала или -1 с ошибкой EAGAIN для сокета. Однако 0 является неоднозначным, поскольку он также возвращается для EOF.

POSIX решил эту проблему с введением O_NONBLOCK, который имеет стандартизированное поведение в разных системах и типах файловых дескрипторов. Поскольку существующие системы обычно хотят избежать любых изменений в поведении, которые могут нарушить обратную совместимость, POSIX определил новый флаг, а не предписывал конкретное поведение для одного из других. Некоторые системы, такие как Linux, обрабатывают все 3 одинаково, а также определяют EAGAIN и EWOULDBLOCK для одного и того же значения, но системы, желающие поддерживать некоторое другое устаревшее поведение для обратной совместимости, могут делать это при использовании более старых механизмов.

Новые программы должны использовать fcntl( ... O_NONBLOCK ... ), как стандартизировано POSIX.

6 голосов
/ 19 июля 2009

Я считаю, fcntl() - это функция POSIX. Где, как ioctl() - это стандартная вещь UNIX. Вот список POSIX io . ioctl() очень специфичен для ядра / драйвера / ОС, но я уверен, что то, что вы используете, работает на большинстве разновидностей Unix. некоторые другие ioctl() вещи могут работать только на определенных ОС или даже на определенных оборотах ядра.

5 голосов
/ 20 июля 2009

Как сказал @Sean, fcntl() в значительной степени стандартизирован и поэтому доступен на разных платформах. Функция ioctl() предшествует fcntl() в Unix, но вообще не стандартизирована. То, что ioctl() сработал для вас на всех актуальных для вас платформах, повезло, но не гарантировано. В частности, имена, используемые для второго аргумента, являются загадочными и ненадежными на разных платформах. Действительно, они часто уникальны для конкретного драйвера устройства, на который ссылается дескриптор файла. (Вызовы ioctl(), используемые для растрового графического устройства, работающего на ICL Perq, работающем PNX (Perq Unix) двадцать лет назад, никогда не переводились ни на что другое, например.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...