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.
timeout jest górną granicą czasu, który upłynie
przed zakończeniem działania funkcji
select(). 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;
sigprocmask(SIG_SETMASK, &sigmask, &origmask);
ready = select(nfds, &readfds, &writefds, &exceptfds, timeout);
sigprocmask(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.
Uwagi linuksowe¶
Wywołanie systemowe
pselect() 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.
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 triku potoku do siebie (gdzie
procedura obsługi sygnału zapisuje bajt do potoku, którego
drugi koniec jest monitorowany przez
select() w głównym
programie).
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¶
Samouczek z dyskusją i przykładami znajduje się w
select_tut(2).
Rzeczy w nieokreślony sposób powiązane z tym można
znaleźć w
accept(2),
connect(2),
poll(2),
read(2),
recv(2),
send(2),
sigprocmask(2),
write(2),
epoll(7),
time(7)
O STRONIE¶
Angielska wersja tej strony pochodzi z wydania 3.40 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> i Robert Luberda
<robert@debian.org>.
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.40 oryginału.