Nicht blockierende UNIX-E/A: O_NONBLOCK vs. FIONBIO

Lesezeit: 4 Minuten

Benutzeravatar von Alex Balashov
Alex Balaschow

In jedem Beispiel und jeder Diskussion, auf die ich im Zusammenhang mit der BSD-Socket-Programmierung stoße, scheint es, dass der empfohlene Weg, einen Dateideskriptor in den nicht blockierenden I/O-Modus zu setzen, die Verwendung von ist O_NONBLOCK Flagge zu fcntl()z.B

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

Ich beschäftige mich seit über zehn Jahren mit der Netzwerkprogrammierung unter UNIX und habe immer die FIONBIO ioctl() Aufruf dazu:

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

Habe nie wirklich darüber nachgedacht, warum. Habe es gerade so gelernt.

Hat jemand einen Kommentar zu den möglichen jeweiligen Vorzügen des einen oder anderen? Ich stelle mir vor, dass sich der Portabilitäts-Locus etwas unterscheidet, weiß aber nicht, inwieweit das so ist ioctl_list(2) spricht nicht zu diesem Aspekt des Individuums ioctl Methoden.

Vor der Standardisierung gab es ioctl(FIONBIO) und fcntl(O_NDELAY), aber diese verhielten sich zwischen Systemen und sogar innerhalb desselben Systems uneinheitlich. Zum Beispiel war es üblich für FIONBIO an Steckdosen zu arbeiten u O_NDELAY um an ttys zu arbeiten, mit vielen Inkonsistenzen für Dinge wie Pipes, Fifos und Geräte. Und wenn Sie nicht wissen, welche Art von Dateideskriptor Sie haben, müssen Sie beide festlegen, um sicherzugehen. Aber auch ein nicht blockierender Lesevorgang ohne verfügbare Daten wurde widersprüchlich angezeigt; Abhängig vom Betriebssystem und dem Typ des Dateideskriptors kann der Lesevorgang 0 oder -1 mit Fehlernummer EAGAIN oder -1 mit Fehlernummer EWOULDBLOCK zurückgeben. Noch heute Einstellung FIONBIO oder O_NDELAY unter Solaris bewirkt, dass ein Lesevorgang ohne Daten 0 auf einem tty oder einer Pipe oder -1 mit errno EAGAIN auf einem Socket zurückgibt. 0 ist jedoch mehrdeutig, da es auch für EOF zurückgegeben wird.

POSIX ging darauf mit der Einführung von ein O_NONBLOCK, das über verschiedene Systeme und Dateideskriptortypen hinweg ein standardisiertes Verhalten aufweist. Da bestehende Systeme normalerweise Verhaltensänderungen vermeiden möchten, die die Abwärtskompatibilität beeinträchtigen könnten, hat POSIX ein neues Flag definiert, anstatt ein bestimmtes Verhalten für eines der anderen vorzuschreiben. Einige Systeme wie Linux behandeln alle 3 gleich und definieren auch EAGAIN und EWOULDBLOCK mit demselben Wert, aber Systeme, die ein anderes Legacy-Verhalten für die Abwärtskompatibilität beibehalten möchten, können dies tun, wenn die älteren Mechanismen verwendet werden.

Neue Programme verwenden sollten fcntl(O_NONBLOCK)wie von POSIX standardisiert.

  • Ich neige dazu, ioctl() dafür zu verwenden, weil es mich nur einen Syscall kostet, um den nicht blockierenden Modus zu aktivieren, anstatt zwei für fcntl(). Darüber hinaus ist die Windows-API ioctlsocket() für diese Funktionalität äquivalent zu ioctl().

    – Wez Furlong

    2. September 2012 um 1:30 Uhr

  • nginx tut dies, wenn es kann, und markiert es mit einem Kommentar “ioctl(FIONBIO) setzt einen nicht blockierenden Modus mit dem einzelnen Syscall.” Jetzt gibt es Accept2, mit dem Sie eine Verbindung akzeptieren und im selben Systemaufruf in den nicht blockierenden Modus versetzen können.

    – Elof

    28. Januar 2014 um 18:18 Uhr


Wie @ Sean sagte, fcntl() ist weitgehend standardisiert und damit plattformübergreifend verfügbar. Das ioctl() Funktion älter fcntl() in Unix, ist aber überhaupt nicht standardisiert. Dass die ioctl() auf allen für Sie relevanten Plattformen für Sie gearbeitet hat, ist glücklicherweise, aber nicht garantiert. Insbesondere die für das zweite Argument verwendeten Namen sind geheimnisvoll und nicht plattformübergreifend zuverlässig. Tatsächlich sind sie oft eindeutig für den bestimmten Gerätetreiber, auf den der Dateideskriptor verweist. (Das ioctl() Aufrufe, die für ein Bitmap-Grafikgerät verwendet wurden, das auf einem ICL Perq mit PNX (Perq Unix) von vor zwanzig Jahren lief, wurden zum Beispiel nirgendwo anders übersetzt.)

Benutzeravatar von EdH
EdH

Ich glaube fcntl() ist eine POSIX-Funktion. Wohingegen ioctl() ist eine Standard-UNIX-Sache. Hier ist eine Liste von POSIX io. ioctl() ist eine sehr Kernel/Treiber/OS-spezifische Sache, aber ich bin sicher, was Sie verwenden, funktioniert auf den meisten Unix-Varianten. manch andere ioctl() Dinge funktionieren möglicherweise nur auf bestimmten Betriebssystemen oder sogar auf bestimmten Versionen des Kernels.

  • Ich habe FIONBIO auf AIX, Solaris, Linux, *BSD und IRIX ohne Probleme verwendet. Aber ja, ich verstehe, dass es zum Beispiel nicht unter Windows funktionieren wird – es ist eine Low-Level-Schnittstelle zu einer sehr spezifischen Kernel-Implementierung. Trotzdem frage ich mich, ob es noch andere Unterscheidungsmerkmale gibt.

    – Alex Balaschow

    19. Juli 2009 um 19:58 Uhr

1420510cookie-checkNicht blockierende UNIX-E/A: O_NONBLOCK vs. FIONBIO

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy