I / O UNIX non bloccante: O_NONBLOCK rispetto a FIONBIO


92

In ogni esempio e discussione che ho incontrato nel contesto della programmazione di socket BSD, sembra che il modo consigliato per impostare un descrittore di file in modalità I / O non bloccante sia usare il O_NONBLOCKflag su fcntl(), ad es.

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

Faccio programmazione di rete in UNIX da oltre dieci anni e ho sempre utilizzato la FIONBIO ioctl()chiamata per farlo:

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

Non ho mai pensato molto al perché. L'ho appena imparato in questo modo.

Qualcuno ha qualche commento sui possibili rispettivi meriti dell'uno o dell'altro? Immagino che il luogo della portabilità differisca in qualche modo, ma non so fino a che punto in quanto ioctl_list(2)non parla di quell'aspetto dei ioctlmetodi individuali .

Risposte:


134

Prima della standardizzazione c'erano ioctl(... FIONBIO... )e fcntl(... O_NDELAY... ), ma questi si comportavano in modo incoerente tra i sistemi e persino all'interno dello stesso sistema. Ad esempio, era comune FIONBIOlavorare su socket e O_NDELAYlavorare su tty, con molte incongruenze per cose come pipe, fifos e dispositivi. E se non sapevi che tipo di descrittore di file avevi, dovresti impostarli entrambi per essere sicuro. Ma in aggiunta, è stata indicata anche una lettura non bloccante senza dati disponibili; a seconda del sistema operativo e del tipo di descrittore di file, la lettura può restituire 0, o -1 con errno EAGAIN o -1 con errno EWOULDBLOCK. Ancora oggi, l'impostazione FIONBIOoO_NDELAYsu Solaris fa sì che una lettura senza dati restituisca 0 su una tty o pipe, o -1 con errno EAGAIN su un socket. Tuttavia 0 è ambiguo poiché viene restituito anche per EOF.

POSIX ha affrontato questo problema con l'introduzione di O_NONBLOCK, che ha un comportamento standardizzato su diversi sistemi e tipi di descrittori di file. Poiché i sistemi esistenti di solito vogliono evitare qualsiasi modifica al comportamento che potrebbe rompere la compatibilità con le versioni precedenti, POSIX ha definito un nuovo flag piuttosto che imporre un comportamento specifico per uno degli altri. Alcuni sistemi come Linux trattano tutti e 3 allo stesso modo e definiscono anche EAGAIN e EWOULDBLOCK con lo stesso valore, ma i sistemi che desiderano mantenere un altro comportamento legacy per la compatibilità con le versioni precedenti possono farlo quando vengono utilizzati i meccanismi precedenti.

I nuovi programmi dovrebbero usare fcntl(... O_NONBLOCK... ), come standardizzato da POSIX.


6
Tendo ad usare ioctl () per questo perché mi costa solo una syscall per abilitare la modalità non bloccante piuttosto che due per fcntl (). Inoltre, l'API ioctlsocket () di Windows è equivalente a ioctl () ai fini di questa funzionalità.
Wez Furlong

nginx lo fa se può, e lo contrassegna con un commento "ioctl (FIONBIO) imposta una modalità non bloccante con la singola syscall." Ora c'è accept2 che ti permette di accettare una connessione e metterla in modalità non bloccante nella stessa syscall.
Eloff

6

Come ha detto @Sean, fcntl()è ampiamente standardizzato e quindi disponibile su tutte le piattaforme. La ioctl()funzione è precedente fcntl()a Unix, ma non è affatto standardizzata. Che la ioctl()lavorato per voi su tutte le piattaforme di rilevanza per voi è fortunato, ma non garantiti. In particolare, i nomi usati per il secondo argomento sono arcani e non affidabili su tutte le piattaforme. In effetti, sono spesso unici per il particolare driver di dispositivo a cui fa riferimento il descrittore di file. (Le ioctl()chiamate utilizzate per un dispositivo grafico a mappatura di bit in esecuzione su un ICL Perq che esegue PNX (Perq Unix) di vent'anni fa non sono mai state tradotte in nessun'altra parte, ad esempio.)


6

Credo fcntl()sia una funzione POSIX. Dove come ioctl()è una cosa UNIX standard. Ecco un elenco di POSIX io . ioctl()è una cosa molto specifica per kernel / driver / sistema operativo, ma sono sicuro che quello che usi funziona sulla maggior parte delle versioni di Unix. alcune altre ioctl()cose potrebbero funzionare solo su determinati sistemi operativi o anche su alcune revisioni del kernel.


Ho usato FIONBIO su AIX, Solaris, Linux, * BSD e IRIX senza problemi. Ma sì, capisco che non funzionerà su Windows, ad esempio: è un'interfaccia di basso livello per un'implementazione del kernel molto specifica. Tuttavia, mi chiedo se ci siano altri fattori di differenziazione.
Alex Balashov
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.