Неблокуючий вхід / вивід UNIX: O_NONBLOCK проти FIONBIO


92

У кожному прикладі та обговоренні, яке я зустрічаю в контексті програмування сокетів 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методів.

Відповіді:


135

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

POSIX вирішив це питання введенням O_NONBLOCK, який стандартизував поведінку в різних системах та типах дескрипторів файлів. Оскільки існуючі системи зазвичай хочуть уникнути будь-яких змін у поведінці, які можуть порушити зворотну сумісність, POSIX визначив новий прапор, а не зобов’язував конкретну поведінку для однієї з інших. Деякі системи, такі як Linux, відносяться до всіх трьох однаково, а також визначають EAGAIN та EWOULDBLOCK до одного і того ж значення, але системи, які бажають підтримувати деякі інші застарілі дії для зворотної сумісності, можуть це робити, коли використовуються старіші механізми.

Нові програми повинні використовувати fcntl(... O_NONBLOCK... ), як стандартизовано POSIX.


6
Я схильний використовувати для цього ioctl (), тому що мені потрібно лише один системний виклик, щоб увімкнути режим неблокування, а не два для fcntl (). Крім того, API ioctlsocket () Windows еквівалентний ioctl () для цілей цієї функціональності.
Вез Ферлонг

nginx робить це, якщо може, і позначає це коментарем "ioctl (FIONBIO) встановлює режим неблокування за допомогою єдиного системного виклику." Тепер є accept2, який дозволяє прийняти підключення та перевести його в неблокуючий режим у тому ж syscall.
Елофф

6

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


6

Я вважаю fcntl(), що це функція POSIX. Де як ioctl()стандартна річ UNIX. Ось список POSIX io . ioctl()це дуже специфічне для ядра / драйвера / ОС, але я впевнений, що те, що ви використовуєте, працює на більшості ароматів Unix. деякі інші ioctl()речі можуть працювати лише на певній ОС або навіть на певних оборотах її ядра.


Я без проблем використовував FIONBIO на AIX, Solaris, Linux, * BSD та IRIX. Але так, я розумію, що це не буде працювати, наприклад, у Windows - це низькорівневий інтерфейс до дуже конкретної реалізації ядра. Тим не менше, мені цікаво, чи є якісь інші диференціюючі фактори.
Олексій Балашов
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.