Неблокирующий Unix и ввода/вывода: о НЕБЛОКОВЫЙ и FIONBIO


в каждом примере и обсуждении, с которыми я сталкиваюсь в контексте программирования сокетов 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 84

3 ответа:

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

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

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

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

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