Provided by: manpages-pl-dev_0.5-1_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 */
#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 || _XOPEN_SOURCE >= 600
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 (np. read(2)) bez blokowania.
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 zapis nie spowoduje
blokady, 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 minimalny interwał który powinien blokować select() czekając na gotowość
deskryptora plików (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-2001).
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-2001 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; zbiory deskryptorów i timeout stają się niezdefiniowane, więc nie należy polegać na ich
zawartości.
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 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 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 i w POSIX.1-2001.
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.
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-2001 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 1003.1-2001 polega na tym, że dla select() i pselect()
należy włączyć <sys/select.h>.
Libc4 i libc5 nie zawierają pliku nagłówkowego <sys/select.h>; ten plik nagłówkowy istnieje w glibc 2.0 i
późniejszych. W glibc 2.0 udostępnia on 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ą.
Uwagi linuksowe
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
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-2001 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), 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 3.52 projektu Linux man-pages. Opis projektu oraz
informacje dotyczące zgłaszania błędów można znaleźć pod adresem http://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ą 3.52 oryginału.
Linux 2012-08-17 SELECT(2)