2009-07-19 16 views
78

In jedem Beispiel und Diskussion im Rahmen der BSD-Socket-Programmierung scheint es, dass die empfohlene Methode zum Festlegen eines Dateideskriptors auf nicht blockierende E/A-Modus ist Flagge zu fcntl(), zBUNIX nonblocking E/A: O_NONBLOCK vs. FIONBIO

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

Ich habe seit über zehn Jahren zu tun Netzwerkprogrammierung in UNIX worden und haben die FIONBIO ioctl() nennen dies immer zu tun pflegen:

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

Nie gab viel Gedanken wirklich, warum. Habe es gerade so gelernt.

Hat jemand irgendwelche Kommentare über die möglichen jeweiligen Vorteile des einen oder anderen? Ich stelle mir vor, die Portabilität Locus unterscheidet sich etwas, aber weiß nicht, in welchem ​​Ausmaß, wie ioctl_list(2) spricht nicht mit diesem Aspekt der einzelnen Methoden ioctl.

Antwort

114

Vor der Standardisierung war es ioctl(. .. FIONBIO ... ) und fcntl( ... O_NDELAY ... ), aber diese verhielten sich inkonsistent zwischen Systemen und sogar innerhalb des gleichen Systems. Zum Beispiel war es üblich, dass FIONBIO an Sockets arbeitete und O_NDELAY, um mit ttys zu arbeiten, mit einer Menge Inkonsistenz für Dinge wie Pipes, Fifos und Geräte. Und wenn Sie nicht wüssten, welche Art von Dateideskriptor Sie hatten, müssten Sie beide festlegen, um sicher zu gehen. Zusätzlich wurde ein nicht-blockierendes Lesen ohne verfügbare Daten ebenfalls inkonsistent angezeigt; abhängig vom Betriebssystem und dem Typ des Dateideskriptors kann der Lesevorgang 0 zurückgeben, oder -1 mit errno EAGAIN oder -1 mit errno EWOULDBLOCK. Selbst heute, wenn FIONBIO oder O_NDELAY auf Solaris gesetzt wird, führt ein Lesen ohne Daten dazu, dass 0 auf einem tty oder einer Pipe zurückgegeben wird, oder -1 mit errno EAGAIN auf einem Socket. Allerdings ist 0 mehrdeutig, da es auch für EOF zurückgegeben wird.

POSIX adressiert dies mit der Einführung von O_NONBLOCK, die standardisiertes Verhalten über verschiedene Systeme und Dateideskriptortypen hat. Da vorhandene Systeme in der Regel Änderungen am Verhalten vermeiden möchten, die die Abwärtskompatibilität beeinträchtigen könnten, definierte POSIX ein neues Flag, anstatt eines bestimmten Verhaltens für eines der anderen festzulegen. Einige Systeme wie Linux behandeln alle 3 gleich und definieren auch EAGAIN und EWOULDBLOCK mit dem gleichen Wert, aber Systeme, die aus Gründen der Abwärtskompatibilität ein anderes veraltetes Verhalten beibehalten möchten, können dies tun, wenn die älteren Mechanismen verwendet werden.

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

+5

Ich neige dazu, ioctl() dafür zu verwenden, weil es mich kostet, nur einen syscall, um nicht-blockierenden Modus statt zwei für fcntl() zu aktivieren. Darüber hinaus entspricht die Windows ioctlsocket() API ioctl() für diese Zwecke. –

+0

nginx tut dies, wenn es möglich ist, 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 annehmen und sie im selben syscall in den nicht blockierenden Modus versetzen können. – Eloff

5

Ich glaube, fcntl() ist eine POSIX-Funktion. Wo als ioctl() ist eine Standard-UNIX-Sache. Hier ist eine Liste von POSIX io. ioctl() ist eine sehr Kern/Treiber/OS-spezifische Sache, aber ich bin sicher, was Sie verwenden, funktioniert auf den meisten Unix-Aromen. einige andere ioctl() Sachen könnten nur auf bestimmten Betriebssystemen oder sogar bestimmten Versionen des Kerns funktionieren.

+0

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

4

Wie @Sean sagte, ist fcntl() weitgehend standardisiert und daher plattformübergreifend verfügbar. Die ioctl() Funktion ist älter als fcntl() in Unix, ist aber überhaupt nicht standardisiert. Dass die ioctl() für Sie auf allen für Sie relevanten Plattformen funktioniert, ist zwar glücklich, aber nicht garantiert. Insbesondere sind die Namen für das zweite Argument geheimnisvoll und nicht plattformübergreifend zuverlässig. Tatsächlich sind sie oft einzigartig für den bestimmten Gerätetreiber, auf den der Dateideskriptor verweist. (Die ioctl() fordert ein Bitmap-Grafikgerät läuft auf einem ICL Perq läuft PNX (Perq Unix) von zwanzig Jahren vor nie sonst nirgendwo anders etwas übersetzt, zum Beispiel.)

Verwandte Themen