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.