select(2) System Calls Manual select(2) NAZWA select, pselect, FD_CLR, FD_ISSET, FD_SET, FD_ZERO, fd_set - synchronicznie zwielokrotnia wejscie/wyjscie BIBLIOTEKA Standardowa biblioteka C (libc, -lc) SKLADNIA #include typedef /* ... */ fd_set; int select(int nfds, fd_set *_Nullable restrict readfds, fd_set *_Nullable restrict writefds, fd_set *_Nullable restrict exceptfds, struct timeval *_Nullable restrict timeout); void FD_CLR(int fd, fd_set *set); int FD_ISSET(int fd, const fd_set *set); void FD_SET(int fd, fd_set *set); void FD_ZERO(fd_set *set); int pselect(int nfds, fd_set *_Nullable restrict readfds, fd_set *_Nullable restrict writefds, fd_set *_Nullable restrict exceptfds, const struct timespec *_Nullable restrict timeout, const sigset_t *_Nullable restrict sigmask); Wymagane ustawienia makr biblioteki glibc (patrz feature_test_macros(7)): pselect(): _POSIX_C_SOURCE >= 200112L OPIS OSTRZEZENIE: select() potrafi monitorowac deskryptory plikow o numerach mniejszych niz FD_SETSIZE (1024) -- limit nierozsadnie niewielki dla wielu wspolczesnych aplikacji -- i ograniczenie to nie zmieni sie. Wszystkie wspolczesne aplikacje powinny w zamian korzystac z poll(2) lub epoll(7), ktore nie maja tego ograniczenia. select() umozliwia programowi monitorowanie wielu deskryptorow plikow i oczekiwanie az jeden lub wiecej deskryptorow bedzie ,,gotowy" na wykonanie pewnej klasy operacji wejscia/wyjscia (np. mozliwy odczyt). Deskryptor pliku jest uwazany za gotowy, jezeli mozliwe jest wykonanie odpowiadajacej operacji wejscia/wyjscia (np. read(2) lub odpowiednio male write(2)) bez blokowania. fd_set Typ struktury mogacej reprezentowac zbior deskryptorow pliku. Zgodnie z POSIX, maksymalna liczba deskryptorow pliku w strukturze fd_set jest wartosc makra FD_SETSIZE. Zbiory deskryptorow pliku Podstawowymi argumentami select() sa trzy ,,zbiory" deskryptorow pliku (zadeklarowane jako typ fd_set), pozwalajace wywolujacemu na oczekiwanie na trzy klasy zdarzen na zadanym zbiorze deskryptorow pliku. Kazdy argument fd_set mozna podac jako NULL, jesli dla odpowiadajacej klasy zdarzen nie maja byc monitorowane deskryptory pliku. Prosze zwrocic szczegolna uwage: Po powrocie, kazdy ze zbiorow deskryptorow pliku jest modyfikowany w miejscu, aby wskazac, ktore z deskryptorow pliku sa aktualnie ,,gotowe". Z tego wzgledu, jesli select() jest uzywany w petli, zbiory musza byc zainicjowane ponownie przed kazdym wywolaniem. Zawartoscia zbiorow deskryptorow pliku mozna manipulowac za pomoca nastepujacych makr: FD_ZERO() Makro czysci (usuwa z niego wszystkie deskryptory pliku) set. Powinno byc uzyte jako pierwszy krok w inicjowaniu zbiorow deskryptorow pliku. FD_SET() Makro dodaje deskryptor pliku fd do zbioru set. Dodanie deskryptora pliku, ktory jest juz obecny w zbiorze, jest instrukcja pusta i nie powoduje bledu. FD_CLR() Makro usuwa deskryptor pliku fd z zbioru set. Usuniecie deskryptora pliku, ktory nie jest obecny w zbiorze jest instrukcja pusta i nie powoduje bledu. FD_ISSET() select() modyfikuje zawartosc zbiorow, zgodnie z regulami opisanymi nizej. Po wywolaniu select(), mozna skorzystac z makra FD_ISSET(), aby sprawdzic, czy deskryptor pliku jest wciaz obecny w zbiorze. FD_ISSET() zwraca wartosc niezerowa, jesli deskryptor pliku fd jest obecny w zbiorze set i zero, gdy nie jest. Argumenty Argumenty select() sa nastepujace: readfds Deskryptory pliku w tym zbiorze sa monitorowane, aby sprawdzic, czy sa gotowe do odczytu. Deskryptor pliku jest gotowy do odczytu, jesli operacja odczytu nie bedzie blokowala; w szczegolnosci, deskryptor pliku jest rowniez gotowy na koncu pliku. Po powrocie select(), readfds zostanie wyczyszczone ze wszystkich deskryptorow pliku, poza gotowymi do odczytu. writefds Deskryptory pliku w tym zbiorze sa monitorowane, aby sprawdzic, czy sa gotowe do zapisu. Deskryptor pliku jest gotowy do zapisu, jesli wszystkie operacje zapisu nie beda blokowaly. Jednak nawet gdy deskryptor pliku jest wskazywany jako zapisywalny, duzy zapis moze wciaz blokowac. Po powrocie select(), writefds zostanie wyczyszczone ze wszystkich deskryptorow pliku, poza gotowymi do zapisu. exceptfds Deskryptory pliku w tym zbiorze sa monitorowane pod katem ,,szczegolnych okolicznosci". Przyklady takich szczegolnych okolicznosci opisano w podreczniku poll(2) odnosnie POLLPRI. Po powrocie select(), exceptfds zostanie wyczyszczone ze wszystkich deskryptorow pliku, poza tymi, dla ktorych zaszly szczegolne okolicznosci. nfds Argument ten powinien byc ustawiony na najwyzszy numer deskryptora z wszystkich trzech zbiorow plus 1. Sprawdzany jest wskazany deskryptor pliku w kazdym zbiorze, do tego limitu (lecz zob. USTERKI). timeout Argument timeout jest struktura timeval (pokazana nizej), okreslajaca interwal, ktory powinien blokowac select(), czekajac na gotowosc deskryptora plikow. Wywolanie bedzie skutkowalo blokada do momentu az: o deskryptor pliku stanie sie dostepny o lub wywolanie zostanie przerwane procedura obslugi sygnalu o albo wywolanie przeterminuje sie Prosze zauwazyc, ze interwal zostanie zaokraglony w gore do dokladnosci zegara, a wystepowanie opoznienia planisty jadra oznacza, ze ten interwal moze byc nieznacznie przekroczony. Jesli oba pola struktury timeval maja wartosc zero, select() niezwlocznie zakonczy prace (jest to przydatne przy odpytywaniu). Jesli timeout jest rowne NULL, select() moze blokowac w nieskonczonosc, czekajac na gotowosc deskryptora pliku. pselect() Wywolanie systemowe pselect() pozwala aplikacjom na bezpieczne oczekiwanie, do momentu uzyskania gotowosci przez deskryptor plikow lub do momentu przechwycenia sygnalu. Funkcjonalnosc funkcji select() i pselect() jest identyczna, jesli pominac trzy roznice: o Funkcja select() uzywa dla parametru timeout typu struct timeval (z sekundami i mikrosekundami), podczas gdy pselect() uzywa typu struct timespec (z sekundami i nanosekundami). o Funkcja select() moze aktualizowac parametr timeout, aby wskazac, jak duzo czasu minelo. Funkcja pselect() nie zmienia tego parametru. o Funkcja select() nie przyjmuje parametru sigmask i zachowuje sie, jak pselect() wywolane z NULL-em przekazanym w sigmask. sigmask jest wskaznikiem do maski sygnalow (zobacz sigprocmask(2)). Jesli nie jest rowne NULL, to pselect() najpierw zastepuje biezaca maske sygnalow maska wskazywana przez sigmask, a nastepnie wywoluje funkcje ,,select", a po jej zakonczeniu odtwarza oryginalna maske sygnalow (jesli sigmask wynosi NULL, to maska sygnalow nie jest modyfikowana podczas wywolania pselect()). Poza roznica w precyzji argumentu timeout, nastepujace wywolanie pselect(): ready = pselect(nfds, &readfds, &writefds, &exceptfds, timeout, &sigmask); jest odpowiednikiem niepodzielnego wykonania nastepujacych funkcji: sigset_t origmask; pthread_sigmask(SIG_SETMASK, &sigmask, &origmask); ready = select(nfds, &readfds, &writefds, &exceptfds, timeout); pthread_sigmask(SIG_SETMASK, &origmask, NULL); Idea pselect() polega na tym, ze gdy chce sie oczekiwac na zdarzenie bedace sygnalem lub czyms na deskryptorze pliku, potrzebny jest atomowy test zapobiegajacy sytuacjom wyscigu. (Przypuscmy, ze procedura obslugi sygnalu ustawia globalny znacznik i konczy dzialanie. Wowczas, test tego znacznika globalnego, po ktorym nastepuje wywolanie select() moze wisiec w nieskonczonosc, gdyby sygnal przybyl natychmiast po tescie, ale przed wywolaniem. Inaczej mowiac, pselect zezwala na, najpierw, zablokowanie sygnalow, nastepnie obsluzenie dostarczonych sygnalow, aby wreszcie wywolac pselect() z pozadanym sigmask, unikajac wyscigu). Przeterminowanie Argument timeout do select() jest struktura nastepujacego typu: struct timeval { time_t tv_sec; /* sekundy */ suseconds_t tv_usec; /* mikrosekundy */ }; Odpowiadajacym argumentem pselect() jest struktura timespec(3). Pod Linuksem funkcja select() modyfikuje timeout, aby odzwierciedlic ilosc nieprzespanego czasu; wiekszosc innych implementacji tego nie robi (POSIX.1 dopuszcza oba te zachowania). Powoduje to problemy, zarowno gdy kod linuksowy odczytujacy timeout zostanie przeniesiony na inne systemy operacyjne, jak i gdy kod przeniesiony pod Linuksa z innych systemow uzywa ponownie struct timeval dla wielu wywolan select() w petli, bez powtornej inicjacji. Prosimy rozwazyc traktowanie wartosci timeout jako niezdefiniowanej po zakonczeniu funkcji select(). WARTOSC ZWRACANA Po pomyslnym zakonczeniu, select() i pselect() zwracaja liczbe deskryptorow w zbiorach deskryptorow (to jest calkowita liczbe bitow ustawiona w readfds, writefds, exceptfds). Wartosc zwracana moze wynosic zero, jesli czas oczekiwania uplynal zanim jakis deskryptor plikow stal sie gotowy. Po bledzie zwracane jest -1 i ustawiane errno, wskazujac blad; zbiory deskryptorow nie sa modyfikowane, a timeout staje sie niezdefiniowane. BLEDY EBADF W jednym ze zbiorow przekazano niepoprawny deskryptor pliku (byc moze deskryptor ten zostal juz zamkniety lub wystapil na nim inny blad). Zob. jednak BLEDY. EINTR Przechwycono sygnal, patrz signal(7). EINVAL nfds jest ujemne lub przekracza limit zasobow RLIMIT_NOFILE (zob. getrlimit(2)). EINVAL Wartosc timeout jest nieprawidlowa. ENOMEM Nie mozna bylo przydzielic pamieci dla wewnetrznych tablic. WERSJE Na niektorych innych systemach Uniksowych select() moze zwrocic blad EAGAIN jesli systemowi nie uda sie przydzielic wewnatrzjadrowych zasobow, zamiast ENOMEM, tak jak robi to Linux. POSIX przewiduje ten blad dla poll(2), lecz nie dla select(). Przenosne programy moga chciec sprawdzac EAGAIN w petli, tak jak dla EINTR. STANDARDY POSIX.1-2024. HISTORIA select() POSIX.1-2001, 4.4BSD (pojawilo sie pierwotnie w 4.2BSD). W ogolnosci jest przenosne do/z systemow nie-BSD wspierajacych sklonowana warstwe gniazd BSD (wlaczajac warianty Systemu V). Jednakze nalezy zauwazyc, ze warianty Systemu V zasadniczo ustawiaja zmienna przeterminowania przed powrotem, ale wariant BSD tego nie robi. pselect() Linux 2.6.16. POSIX.1g, POSIX.1-2001. Wczesniej byl on emulowany w glibc (patrz rowniez USTERKI). fd_set POSIX.1-2001. UWAGI Nastepujacy naglowek zapewnia rowniez typ fd_set: . fd_set jest buforem o stalym rozmiarze. Wykonanie FD_CLR() lub FD_SET() z ujemna wartoscia fd albo z wartoscia wieksza lub rowna FD_SETSIZE spowoduje zachowanie niezdefiniowane. Ponadto POSIX wymaga, by fd byl prawidlowym deskryptorem pliku. Na dzialanie select() i pselect() nie wplywa znacznik O_NONBLOCK. Sztuczka ,,potoku do siebie" W systemach, ktore nie maja pselect() niezawodne (i bardziej przenosne) przechwytywanie sygnalow mozna osiagnac, uzywajac sztuczki w postaci ,,potoku do siebie". W tej technice procedura obslugi sygnalu zapisuje bajt do potoku, ktorego drugi koniec jest monitorowany przez select() w glownym programie. Aby uniknac mozliwego zablokowania przy pisaniu do potoku ktory moze byc pelny lub czytaniu z potoku ktory moze byc pusty, przy czytaniu z i pisaniu do potoku uzywane jest nieblokujace wejscie/wyjscie. Emulowanie usleep(3) Przed powstaniem usleep(3) niektore programy uzywaly wywolania select() z wszystkimi trzema zbiorami pustymi, z nfds rownym zeru i niezerowym timeout. Jest to calkiem przenosny sposob pauzowania z dokladnoscia subsekundowa. Relacja pomiedzy powiadomieniami select() i poll() W zrodlach jadra Linux mozna znalezc nastepujace definicje pokazujace relacje pomiedzy powiadomieniami o warunkach: odczytu, zapisu i szczegolnych okolicznosci select(), a powiadomieniami zdarzen zapewnianymi przez poll(2) i epoll(7): #define POLLIN_SET (EPOLLRDNORM | EPOLLRDBAND | EPOLLIN | EPOLLHUP | EPOLLERR) /* Gotowosc do odczytu */ #define POLLOUT_SET (EPOLLWRBAND | EPOLLWRNORM | EPOLLOUT | EPOLLERR) /* Gotowosc do zapisu */ #define POLLEX_SET (EPOLLPRI) /* Szczegolne okolicznosci */ Aplikacje wielowatkowe Jesli deskryptor plikow monitorowany przez select() zostanie zamkniety w innym watku, to rezultat jest nieokreslony. Na niektorych systemach uniksowych select() odblokuje go i powroci wskazujac, ze dany deskryptor plikow jest gotowy (kolejne operacje wyjscia/wyjscia prawdopodobnie zakoncza sie bledem, chyba ze inny proces otworzy ponownie deskryptor plikow w czasie pomiedzy powrotem select() a wykonanie operacji wejscia/wyjscia. W systemie Linux (i czesci innych) zamkniecie deskryptora pliku w innym watku nie wplynie na select(). Podsumowujac, kazda aplikacja polegajaca na konkretnym zachowaniu w takim przypadku musi byc uznana za bledna. Roznice biblioteki C/jadra Jadro Linux pozwala deskryptorowi pliku ustawic dowolny rozmiar, na podstawie dlugosci zbiorow do sprawdzenia z wartosci nfds. Jednak w implementacji glibc, typ fd_set ma staly rozmiar. Zob. tez USTERKI. Interfejs pselect() opisany w niniejszym podreczniku jest zaimplementowany w glibc. Stojace za nim linuksowe wywolanie systemowe nazywa sie pselect6(). Cechuje go nieco inne zachowanie od opisywanej funkcji opakowujacej glibc. Wywolanie systemowe pselect6() pod Linuksem modyfikuje argument timeout. Jednakze funkcja glibc ukrywa to zachowanie przez uzycie dla argumentu timeout lokalnej zmiennej, ktora jest przekazywana do wywolania systemowego. Dlatego pselect() z glibc nie zmienia argumentu timeout, co jest zachowaniem wymaganym przez POSIX.1-2001. Ostatnim argumentem wywolania systemowego pselect6() nie jest wskaznik sigset_t * lecz struktura postaci: struct { const kernel_sigset_t *ss; /* Wskaznik do zestawu sygnalow */ size_t ss_len; /* Rozmiar (w bajtach) obiektu, na ktory wskazuje 'ss' */ }; Pozwala to wywolaniu systemowemu pobrac oba wskazniki do zestawu sygnalowego wraz z rozmiarem, biorac pod uwage, ze wiekszosc architektur obsluguje maksymalnie 6 argumentow do wywolania systemowego. Opis roznic pomiedzy podejsciem jadra i libc do zestawu sygnalow znajduje sie w podreczniku sigprocmask(2). Historyczne detale glibc glibc 2.0 dostarczala niepoprawna wersje pselect(), ktora nie przyjmowala argumentu sigmask. Od glibc 2.1 do glibc 2.2.1 konieczne bylo zdefiniowanie _GNU_SOURCE, aby uzyskac deklaracje pselect() z . USTERKI POSIX pozwala implementacji zdefiniowac gorny limit zakresu deskryptorow plikow, ktore mozna podac w zbiorze deskryptora pliku, rozglaszany stala FD_SETSIZE. Jadro Linux nie wymusza stalego limitu, lecz implementacja glibc czyni fd_set typem o stalym rozmiarze, z FD_SETSIZE zdefiniowanym jako 1024 i makrami FD_*() dzialajacymi zgodnie z tym limitem. Aby monitorowac deskryptory plikow wieksze niz 1023, nalezy w zamian uzyc poll(2) lub epoll(7). Implementacja argumentow fd_set jako argumentow wartosc-wynik jest bledem projektowym, ktorego uniknieto w poll(2) i epoll(7). Zgodnie z POSIX, select() powinien sprawdzac wszystkie podane deskryptory pliku w trzech zbiorach deskryptorow pliku, do limitu nfds-1. Jednak biezaca implementacja ignoruje wszelkie deskryptory pliku w tych zbiorach, ktore sa wyzsze od maksymalnego numeru aktualnie otwartego przez proces deskryptora pliku. Zgodnie z POSIX, w przypadku wystapienia takiego deskryptora pliku w jednym z zbiorow, powinien wystapic blad EBADF. Od glibc 2.1, glibc dostarczala emulacje pselect(), ktora byla zaimplementowana przy uzyciu sigprocmask(2) i select(). Implementacja ta pozostaje podatna na wiele bledow wyscigow (race conditions), ktorych unikniecie stanowilo idee funkcji pselect(). Nowsze wersje glibc uzywaja (wolnego od wyscigow) wywolania systemowego, jesli tylko jadro dostarcza takiego wywolania. W Linuksie select() moze raportowac deskryptory plikow gniazd jako ,,dostepne do czytania", podczas gdy kolejne czytania zostana zablokowane. Moze to sie zdarzyc na przyklad wtedy, gdy dane nadeszly, ale podczas sprawdzania okazalo sie, ze maja zla sume kontrolna i zostaly odrzucone. Moga wystapic takze inne sytuacje, w ktorych deskryptor pliku jest blednie raportowany jako gotowy. Dlatego uzywanie O_NONBLOCK na gniazdach, ktore nie powinny sie blokowac moze byc bezpieczniejsze. Pod Linuksem wywolanie select() zmienia wartosc timeout takze wtedy, gdy zostanie przerwane przez procedure obslugi sygnalu (tj. zostanie zwrocony blad EINTR). POSIX.1 nie pozwala na takie zachowanie. Wywolanie systemowe pselect() pod Linuksem zachowuje sie tak samo, ale funkcja opakowujaca biblioteki glibc ukrywa to zachowanie, kopiujac wartosc timeout do wewnetrznej lokalnej zmiennej i przekazujac te zmienna do wywolania systemowego. PRZYKLADY #include #include #include #include int main(void) { int retval; fd_set rfds; struct timeval tv; /* Obserwacja stdin (fd 0) i sprawdzanie kiedy ma wejscie. */ FD_ZERO(&rfds); FD_SET(0, &rfds); /* Czekanie do pieciu sekund. */ tv.tv_sec = 5; tv.tv_usec = 0; retval = select(1, &rfds, NULL, NULL, &tv); /* Nie nalezy juz polegac na wartosci tv! */ if (retval == -1) perror("select()"); else if (retval) printf("Dane sa juz dostepne.\n"); /* FD_ISSET(0, &rfds) bedzie prawdziwy. */ else printf("Brak danych w ciagu pieciu sekund.\n"); exit(EXIT_SUCCESS); } ZOBACZ TAKZE accept(2), connect(2), poll(2), read(2), recv(2), restart_syscall(2), send(2), sigprocmask(2), write(2), timespec(3), epoll(7), time(7) Samouczek z dyskusja i przykladami znajduje sie w select_tut(2). TLUMACZENIE Tlumaczenie niniejszej strony podrecznika: Przemek Borys , Robert Luberda i Michal Kulach Niniejsze tlumaczenie jest wolna dokumentacja. Blizsze informacje o warunkach licencji mozna uzyskac zapoznajac sie z GNU General Public License w wersji 3 lub nowszej. Nie przyjmuje sie ZADNEJ ODPOWIEDZIALNOSCI. Bledy w tlumaczeniu strony podrecznika prosimy zglaszac na adres listy dyskusyjnej . Linux man-pages 6.18 8 lutego 2026 r. select(2)