Provided by: manpages-pl-dev_0.7-2_all 

NAZWA
select, pselect, FD_CLR, FD_ISSET, FD_SET, FD_ZERO - synchroniczne zwielokrotnianie wejście/wyjście
SKŁADNIA
/* Zgodnie z POSIX.1-2001, POSIX.1-2008 */
#include <sys/select.h>
/* Zgodnie z wcześniejszymi standardami */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);
#include <sys/select.h>
int pselect(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, const struct timespec *timeout,
const sigset_t *sigmask);
Wymagane ustawienia makr biblioteki glibc (patrz feature_test_macros(7)):
pselect(): _POSIX_C_SOURCE >= 200112L
OPIS
select() i pselect() umożliwiają programowi monitorowanie wielu deskryptorów plików i oczekiwanie aż
jeden lub więcej deskryptorów będzie "gotowy" na wykonanie pewnej klasy operacji wejścia/wyjścia (np.
możliwy odczyt). Deskryptor pliku jest uważany za gotowy, jeżeli możliwe jest wykonanie odpowiadającej
operacji wejścia/wyjścia (np. read(2) bez blokowania lub odpowiednio małe write(2)).
select() może monitorować wyłącznie numery deskryptorów plików mniejsze niż FD_SETSIZE; poll(2) nie
posiada tego ograniczenia. Zob. BŁĘDY.
Funkcjonalność funkcji select() i pselect() jest identyczna, jeśli pominąć trzy różnice:
(i) Funkcja select() używa dla parametru timeout typu struct timeval (z sekundami i mikrosekundami),
podczas gdy pselect() używa typu struct timespec (z sekundami i nanosekundami).
(ii) Funkcja select() może aktualizować parametr timeout, aby wskazać, jak dużo czasu minęło. Funkcja
pselect() nie zmienia tego parametru.
(iii) Funkcja select() nie przyjmuje parametru sigmask i zachowuje się, jak pselect() wywołane z NULL-em
przekazanym w sigmask.
Podglądane są trzy niezależne zestawy deskryptorów. Te, które są wymienione w readfds, będą obserwowane w
celu dowiedzenia się, czy nie ma tam jakichś znaków dostępnych do czytania (dokładniej, aby dowiedzieć
się, czy read nie spowoduje zablokowania, deskryptor pliku jest również przygotowany na koniec pliku).
Deskryptory wymienione w writefds będą obserwowane w celu dowiedzenia się, czy jest dostępna przestrzeń
do zapisu (choć duże zapisy wciąż mogą być blokowane), a deskryptory wymienione w exceptfds będą
obserwowane w celu dowiedzenia się, czy nie ma na nich wyjątku. Przy wyjściu, zbiory te są modyfikowane,
wskazując, które z deskryptorów zmieniły status. Każdy z tych trzech zbiorów deskryptorów plików może być
przekazany jako NULL, jeżeli dla żadnego deskryptora pliku na ma potrzeby obserwowania odpowiedniej klasy
zdarzeń.
Do obsługi tych zbiorów udostępnione są cztery makra: FD_ZERO() czyści zbiór; FD_SET() i FD_CLR() dodają
lub usuwają ze zbioru podany deskryptor; FD_ISSET() sprawdza, czy deskryptor jest częścią zbioru. Jest to
przydatne po zakończeniu select().
nfds jest najwyższym numerem deskryptora z wszystkich trzech zbiorów plus 1.
Argument timeout określa interwał, który powinien blokować select(), czekając na gotowość deskryptora
plików. Wywołanie będzie skutkowało blokadą do momentu aż:
* deskryptor pliku stanie się dostępny
* lub wywołanie zostanie przerwane procedurą obsługi sygnału
* albo wywołanie przeterminuje się
Proszę zauważyć, że interwał zostanie zaokrąglony w górę do dokładności zegara, a występowanie opóźnienia
planisty jądra oznacza, że ten interwał może być nieznacznie przekroczony. Jeśli oba pola struktury
timeval mają wartość zero, select() zakończy pracę natychmiast jest to przydatne w uwspólnianiu). Jeśli
timeout jest równe NULL (brak czasu przeterminowania), select() może blokować w nieskończoność.
sigmask jest wskaźnikiem do maski sygnałów (zobacz sigprocmask(2)). Jeśli nie jest równe NULL, to
pselect() najpierw zastępuje bieżącą maskę sygnałów maską wskazywaną przez sigmask, a następnie wywołuje
funkcję "select", a po jej zakończeniu odtwarza oryginalną maskę sygnałów.
Poza różnicą w precyzji argumentu timeout, następujące wywołanie pselect():
ready = pselect(nfds, &readfds, &writefds, &exceptfds,
timeout, &sigmask);
jest odpowiednikiem niepodzielnego wykonania następujących 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, że gdy chce się oczekiwać na zdarzenie będące sygnałem lub czymś na
deskryptorze pliku, potrzebny jest atomowy test zapobiegający sytuacjom wyścigu. (Przypuśćmy, że
procedura obsługi sygnału ustawia globalny znacznik i kończy działanie. Wówczas, test tego znacznika
globalnego, po którym następuje wywołanie select() może wisieć w nieskończoność, gdyby sygnał przybył
natychmiast po teście, ale przed wywołaniem. Inaczej mówiąc, pselect zezwala na, najpierw, zablokowanie
sygnałów, następnie obsłużenie dostarczonych sygnałów, aby wreszcie wywołać pselect() z pożądanym
sigmask, unikając wyścigu).
Przeterminowanie
Struktury czasu, których to dotyczy, są zdefiniowane w <sys/time.h> i wyglądają następująco
struct timeval {
long tv_sec; /* sekundy */
long tv_usec; /* mikrosekundy */
};
i
struct timespec {
long tv_sec; /* sekundy */
long tv_nsec; /* nanosekundy */
};
(Jednakże zobacz poniżej uwagi dotyczące POSIX.1).
Niektóre programy wywołują select() z wszystkimi trzema zbiorami pustymi, z nfds równym zeru i niezerowym
timeout. Jest to całkiem przenośny sposób pauzowania z dokładnością subsekundową.
Pod Linuksem funkcja select() modyfikuje timeout, aby odzwierciedlić ilość nieprzespanego czasu;
większość innych implementacji tego nie robi (POSIX.1 dopuszcza oba te zachowania). Powoduje to problemy,
zarówno gdy kod linuksowy odczytujący timeout zostanie przeniesiony na inne systemy operacyjne, jak i gdy
kod przeniesiony pod Linuksa z innych systemów używa ponownie struktury timeval dla wielu wywołań
select() w pętli, bez powtórnej inicjacji. Prosimy rozważyć traktowanie wartości timeout jako
niezdefiniowanej po zakończeniu funkcji select().
WARTOŚĆ ZWRACANA
Po pomyślnym zakończeniu, select() i pselect() zwracają liczbę deskryptorów w zbiorach deskryptorów (to
jest całkowitę liczbę bitów ustawioną w readfds, writefds, exceptfds). Może ona być zerowa, jeśli nastąpi
przeterminowanie, nim coś ciekawego się zdarzy. Po błędzie, zwracane jest -1 i odpowiednio ustawiane
errno aby wskazać błąd; zbiory deskryptorów nie są modyfikowane, a timeout staje się niezdefiniowane.
BŁĘDY
EBADF W jednym ze zbiorów przekazano niepoprawny deskryptor pliku (Być może deskryptor ten został już
zamknięty lub wystąpił na nim inny błąd).
EINTR Przechwycono sygnał, patrz signal(7).
EINVAL nfds jest ujemne lub przekracza limit zasobów RLIMIT_NOFILE (zob. getrlimit(2)).
EINVAL Wartość timeout jest nieprawidłowa.
ENOMEM Nie można było przydzielić pamięci dla wewnętrznych tablic.
WERSJE
pselect() został dodany w wersji 2.6.16 jądra Linuksa. Wcześniej pselect() był emulowany w glibc (patrz
również BŁĘDY IMPLEMENTACJI).
ZGODNE Z
select() jest zgodny z POSIX.1-2001, POSIX.1-2008 i BSD 4.4 (funkcja select() pojawiła się pierwotnie w
BSD 4.2). W ogólności jest przenośne do/z systemów nie-BSD wspierających sklonowaną warstwę gniazd BSD
(włączając warianty Systemu V). Jednakże należy zauważyć, że warianty Systemu V zasadniczo ustawiają
zmienną timeout przed zakończeniem, ale wariant BSD tego nie robi.
pselect() jest zdefiniowany w POSIX.1g, w POSIX.1-2001 i w POSIX.1-2008.
UWAGI
fd_set jest buforem o stałym rozmiarze. Wykonanie FD_CLR() lub FD_SET() z ujemną wartością fd albo z
wartością większą lub równą FD_SETSIZE spowoduje zachowanie niezdefiniowane. Ponadto POSIX wymaga, by fd
był prawidłowym deskryptorem pliku.
Na niektórych innych systemach Uniksowych select() może zwrócić błąd EAGAIN jeśli systemowi nie uda się
przydzielić wewnątrzjądrowych zasobów, zamiast ENOMEM, tak jak robi to Linux. POSIX przewiduje ten błąd
dla poll(2), lecz nie dla select(). Przenośne programy mogą chcieć sprawdzać EAGAIN w pętli, tak jak dla
EINTR.
Jeśli chodzi o używane typy, klasyczna sytuacja polega na tym, że oba pola struktury timeval są typu long
(jak pokazano powyżej), a sama struktura jest zdefiniowana w <sys/time.h>. W POSIX.1 wygląda to
następująco:
struct timeval {
time_t tv_sec; /* sekundy */
suseconds_t tv_usec; /* mikrosekundy */
};
przy czym struktura jest zdefiniowana w <sys/select.h>, a typy time_t i suseconds_t zdefiniowano w
<sys/types.h>.
Jeśli chodzi o prototypy opisywanych funkcji, to klasyczna sytuacja polega na tym, że dla select() należy
włączyć <time.h>, natomiast sytuacja z POSIX.1 polega na tym, że dla select() i pselect() należy włączyć
<sys/select.h>.
W glibc 2.0 <sys/select.h> udostępnia bezwarunkowo błędny prototyp dla pselect(). W glibc 2.1 aż do 2.2.1
udostępnia on pselect(), jeżeli zdefiniowane jest _GNU_SOURCE. Od glibc 2.2.2 wymagania są takie, jak
pokazano powyżej w rozdziale SKŁADNIA.
Aplikacje wielowątkowe
Jeśli deskryptor plików monitorowany przez select() zostanie zamknięty w innym wątku, to rezultat jest
nieokreślony. Na niektórych systemach uniksowych select() odblokuje go i powróci wskazując, że dany
deskryptor plików jest gotowy (kolejne operacje wyjścia/wyjścia prawdopodobnie zakończą się błędem, chyba
że otwarty zostanie inny deskryptor plików w czasie pomiędzy powrotem select() a wykonanie operacji
wejścia/wyjścia. W systemie Linux (i części innych) zamknięcie deskryptora pliku w innym wątku nie
wpłynie na select(). Podsumowując, każda aplikacja polegająca na konkretnym zachowaniu w takim przypadku
musi być uznana za błędną.
Różnice biblioteki C/jądra
Jądro Linux pozwala deskryptorowi pliku ustawić dowolny rozmiar, na podstawie długości zestawów do
sprawdzenia z wartości nfds. Jednak w implementacji glibc, typ fd_set ma stały rozmiar. Zob. też BŁĘDY.
Interfejs pselect() opisany w niniejszym podręczniku jest zaimplementowany w glibc. Stojące za nim
linuksowe wywołanie systemowe nazywa się pselect6(). Cechuje go nieco inne zachowanie od opisywanej
funkcji opakowującej glibc.
Wywołanie systemowe pselect6() pod Linuksem modyfikuje argument timeout. Jednakże funkcja glibc ukrywa to
zachowanie przez użycie dla argumentu timeout lokalnej zmiennej, która jest przekazywana do wywołania
systemowego. Dlatego pselect() z glibc nie zmienia argumentu timeout, co jest zachowaniem wymaganym przez
POSIX.1-2001.
Ostatnim argumentem wywołania systemowego pselect6() nie jest wskaźnik sigset_t * lecz struktura postaci:
struct {
const sigset_t *ss; /* Pointer to signal set */
size_t ss_len; /* Size (in bytes) of object pointed
to by 'ss' */
};
Pozwala to wywołaniu systemowemu pobrać oba wskaźniki do zestawu sygnałowego wraz z rozmiarem, biorąc pod
uwagę, że większość architektur obsługuje maksymalnie 6 argumentów do wywołania systemowego.
BŁĘDY IMPLEMENTACJI
POSIX pozwala implementacji zdefiniować górny limit zakresu deskryptorów plików, które można podać w
zestawie deskryptora pliku, rozgłaszany stałą FD_SETSIZE. Jądro Linux nie wymusza stałego limitu, lecz
implementacja glibc czyni fd_set typem o stałym rozmiarze, z FD_SETSIZE zdefiniowanym jako 1024 i makrami
FD_*() działającymi zgodnie z tym limitem. Aby monitorować deskryptory plików większe niż 1023, należy w
zamian użyć poll(2).
Glibc 2.0 dostarczała wersję pselect(), która nie przyjmowała argumentu sigmask.
Od wersji 2.1 glibc dostarczał emulację pselect(), która była zaimplementowana przy użyciu sigprocmask(2)
i select(). Implementacja ta pozostaje podatna na wiele błędów wyścigów (race conditions), których
uniknięcie stanowiło ideę funkcji pselect(). Nowsze wersje glibc używają (wolnego od wyścigów) wywołania
systemowego, jeśli tylko jądro dostarcza takiego wywołania.
W systemach, które nie mają pselect() niezawodne (i bardziej przenośne) przechwytywanie sygnałów można
osiągnąć, używając sztuczki w postaci "potoku do siebie". W tej technice procedura obsługi sygnału
zapisuje bajt do potoku, którego drugi koniec jest monitorowany przez select() w głównym programie. Aby
uniknąć możliwego zablokowania przy pisaniu do potoku który może być pełny lub czytaniu z potoku który
może być pusty, przy czytaniu z i pisaniu do potoku używane jest nieblokujące wejście/wyjście.
Pod Linuksem select() może raportować deskryptory plików gniazd jako "dostępne do czytania", podczas gdy
kolejne czytania zostaną zablokowane. Może to się zdarzyć na przykład wtedy, gdy dane nadeszły, ale
podczas sprawdzania okazało się, że mają złą sumę kontrolną i zostały odrzucone. Mogą wystąpić także inne
sytuacje, w których deskryptor pliku jest błędnie raportowany jako gotowy. Dlatego używanie O_NONBLOCK na
gniazdach, które nie powinny się blokować może być bezpieczniejsze.
Pod Linuksem wywołanie select() zmienia wartość timeout także wtedy, gdy zostanie przerwane przez
procedurę obsługi sygnału (tj. zostanie zwrócony błąd EINTR). POSIX.1 nie pozwala na takie zachowanie.
Wywołanie systemowe pselect() pod Linuksem zachowuje się tak samo, ale funkcja opakowująca biblioteki
glibc ukrywa to zachowanie, kopiując wartość timeout do wewnętrznej lokalnej zmiennej i przekazując tę
zmienną do wywołania systemowego.
PRZYKŁAD
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int
main(void)
{
fd_set rfds;
struct timeval tv;
int retval;
/* Obserwacja stdin (fd 0) i sprawdzanie kiedy ma wejście. */
FD_ZERO(&rfds);
FD_SET(0, &rfds);
/* Czekanie nie dłużej niż 5 sekund. */
tv.tv_sec = 5;
tv.tv_usec = 0;
retval = select(1, &rfds, NULL, NULL, &tv);
/* Nie należy już polegać na wartości tv! */
if (retval == -1)
perror("select()");
else if (retval)
printf("Dane są już dostępne.\n");
/* FD_ISSET(0, &rfds) będzie prawdziwy. */
else
printf("Brak danych w ciągu 5 sekund.\n");
exit(EXIT_SUCCESS);
}
ZOBACZ TAKŻE
accept(2), connect(2), poll(2), read(2), recv(2), restart_syscall(2), send(2), sigprocmask(2), write(2),
epoll(7), time(7)
Samouczek z dyskusją i przykładami znajduje się w select_tut(2).
O STRONIE
Angielska wersja tej strony pochodzi z wydania 4.07 projektu Linux man-pages. Opis projektu, informacje
dotyczące zgłaszania błędów, oraz najnowszą wersję oryginału można znaleźć pod adresem
https://www.kernel.org/doc/man-pages/.
TŁUMACZENIE
Autorami polskiego tłumaczenia niniejszej strony podręcznika man są: Przemek Borys (PTM)
<pborys@dione.ids.pl>, Robert Luberda <robert@debian.org> i Michał Kułach <michal.kulach@gmail.com>.
Polskie tłumaczenie jest częścią projektu manpages-pl; uwagi, pomoc, zgłaszanie błędów na stronie
http://sourceforge.net/projects/manpages-pl/. Jest zgodne z wersją 4.07 oryginału.
Linux 2016-03-15 SELECT(2)