2009-07-19 7 views
78

W każdym przykładzie i dyskusji, którą prowadzę w kontekście programowania gniazd BSD, wydaje się, że zalecanym sposobem ustawienia deskryptora pliku na niezabezpieczony tryb wejścia/wyjścia jest użycie O_NONBLOCK flagę do fcntl(), npUNIX nonblocking I/O: O_NONBLOCK vs. FIONBIO

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

Robiłem programowania sieciowego w systemie UNIX przez ponad dziesięć lat, i zawsze stosować połączenia FIONBIO ioctl() to zrobić:

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

naprawdę nigdy nie dał wiele myśli, dlaczego. Nauczyłem się tego w ten sposób.

Czy ktoś ma jakiś komentarz na temat możliwych zalet jednego lub drugiego? Wyobrażam sobie, że lokalizacja przenośności różni się nieco, ale nie wiem do jakiego stopnia jako ioctl_list(2) nie mówi się o tym aspekcie indywidualnych metod.

Odpowiedz

114

Przed normalizacji było ioctl(. .. FIONBIO ... ) i fcntl( ... O_NDELAY ... ), ale zachowywały się niespójnie między systemami, a nawet w obrębie tego samego systemu. Na przykład, powszechne było, aby FIONBIO pracował na gniazdach i O_NDELAY, aby pracować na tty, z wieloma niespójnościami dla rzeczy takich jak rury, fifos i urządzenia. A jeśli nie wiesz, jaki rodzaj deskryptora pliku miałeś, musisz ustawić oba, aby mieć pewność. Ponadto dodatkowo brak spójnych odczytów bez dostępnych danych wskazano również niekonsekwentnie; w zależności od systemu operacyjnego i typu deskryptora pliku odczyt może zwrócić 0 lub -1 z errno EAGAIN lub -1 z errno EWOULDBLOCK. Nawet dzisiaj ustawienie FIONBIO lub O_NDELAY w systemie Solaris powoduje, że odczyt bez danych zwraca 0 na tty lub potoku, lub -1 z errno EAGAIN na gnieździe. Jednak 0 jest niejednoznaczne, ponieważ jest również zwracane dla EOF.

POSIX rozwiązał ten problem wprowadzając O_NONBLOCK, który ustandaryzował zachowanie w różnych systemach i typach deskryptorów plików. Ponieważ istniejące systemy zwykle chcą uniknąć jakichkolwiek zmian w zachowaniu, które mogłyby przełamać kompatybilność wsteczną, POSIX zdefiniował nową flagę, zamiast nakazać zachowanie określonego dla jednego z pozostałych. Niektóre systemy, takie jak Linux, traktują wszystkie trzy takie same, a także definiują EAGAIN i EWOULDBLOCK na tę samą wartość, ale systemy, które chcą zachować inne starsze zachowanie w celu zapewnienia kompatybilności wstecznej, mogą to zrobić, gdy używane są starsze mechanizmy.

Nowe programy powinny używać fcntl( ... O_NONBLOCK ... ), w standardzie POSIX.

+5

Mam tendencję do używania ioctl(), ponieważ kosztuje mnie tylko jedna syscall, aby włączyć tryb bez blokowania zamiast dwóch dla fcntl(). Ponadto, Windows ioctlsocket() jest odpowiednikiem ioctl() dla celów tej funkcji. –

+0

nginx robi to, jeśli potrafi i zaznacza je komentarzem "ioctl (FIONBIO) ustawia tryb bez blokowania za pomocą pojedynczej syscall." Teraz istnieje accept2, który pozwala ci zaakceptować połączenie i umieścić go w trybie bez blokowania w tej samej syscall. – Eloff

5

Wierzę, że fcntl() jest funkcją POSIX. Gdzie jako ioctl() jest standardowa rzecz UNIX. Oto lista POSIX io. ioctl() to bardzo specyficzna dla jądra/sterownika/OS, ale jestem pewien, że to, co używasz działa na większości smaków Uniksa. niektóre inne rzeczy mogą działać tylko na niektórych systemach operacyjnych lub nawet na niektórych obrotach jądra.

+0

Użyłem FIONBIO w systemach AIX, Solaris, Linux, * BSD i IRIX bez żadnych problemów. Ale tak, rozumiem, że na przykład nie będzie działać na Windowsie - jest to interfejs niskiego poziomu do bardzo specyficznej implementacji jądra. Zastanawiam się jednak, czy są jakieś inne czynniki różnicujące. –

4

Jak powiedział @Sean, fcntl() jest w dużym stopniu znormalizowany, a zatem dostępny na wielu platformach. Funkcja ioctl() poprzedza fcntl() w systemie Unix, ale nie jest w ogóle ustandaryzowana. To, że ioctl() pracował dla Ciebie na wszystkich platformach, które są dla ciebie ważne, ma szczęście, ale nie jest gwarantowane. W szczególności nazwy używane dla drugiego argumentu są tajemnicze i nie są wiarygodne na różnych platformach. W rzeczywistości często są one unikalne dla konkretnego sterownika urządzenia, do którego odwołuje się deskryptor pliku. (The ioctl() połączenia używany przez bitowej odwzorowany urządzenia graficznej działa na ICL Perq systemem PNX (Perq Unix) dwadzieścia lat temu nigdy przeliczonych na cokolwiek innego gdziekolwiek indziej, na przykład).