Scroll to navigation

SELECT(2) Podręcznik programisty Linuksa SELECT(2)

NAZWA

select, pselect, FD_CLR, FD_ISSET, FD_SET, FD_ZERO - synchroniczne zwielokrotnianie wejście/wyjście

SKŁADNIA

/* According to POSIX.1-2001, POSIX.1-2008 */
#include <sys/select.h>
/* According to earlier standards */
#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() and pselect() allow a program to monitor multiple file descriptors, waiting until one or more of the file descriptors become "ready" for some class of I/O operation (e.g., input possible). A file descriptor is considered ready if it is possible to perform a corresponding I/O operation (e.g., read(2), or a sufficiently small write(2)) without blocking.

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.

Three independent sets of file descriptors are watched. The file descriptors listed in readfds will be watched to see if characters become available for reading (more precisely, to see if a read will not block; in particular, a file descriptor is also ready on end-of-file). The file descriptors in writefds will be watched to see if space is available for write (though a large write may still block). The file descriptors in exceptfds will be watched for exceptional conditions. (For examples of some exceptional conditions, see the discussion of POLLPRI in poll(2).)

On exit, each of the file descriptor sets is modified in place to indicate which file descriptors actually changed status. (Thus, if using select() within a loop, the sets must be reinitialized before each call.)

Each of the three file descriptor sets may be specified as NULL if no file descriptors are to be watched for the corresponding class of events.

Four macros are provided to manipulate the sets. FD_ZERO() clears a set. FD_SET() and FD_CLR() add and remove a given file descriptor from a set. FD_ISSET() tests to see if a file descriptor is part of the set; this is useful after select() returns.

nfds should be set to the highest-numbered file descriptor in any of the three sets, plus 1. The indicated file descriptors in each set are checked, up to this limit (but see BUGS).

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
An invalid file descriptor was given in one of the sets. (Perhaps a file descriptor that was already closed, or one on which an error has occurred.) However, see BUGS.
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.

The operation of select() and pselect() is not affected by the O_NONBLOCK flag.

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.

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.

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.

Correspondence between select() and poll() notifications

Within the Linux kernel source, we find the following definitions which show the correspondence between the readable, writable, and exceptional condition notifications of select() and the event notifications provided by poll(2) and epoll(7):


#define POLLIN_SET  (EPOLLRDNORM | EPOLLRDBAND | EPOLLIN |
                     EPOLLHUP | EPOLLERR)
                   /* Ready for reading */
#define POLLOUT_SET (EPOLLWRBAND | EPOLLWRNORM | EPOLLOUT |
                     EPOLLERR)
                   /* Ready for writing */
#define POLLEX_SET  (EPOLLPRI)
                   /* Exceptional condition */


Aplikacje wielowątkowe

If a file descriptor being monitored by select() is closed in another thread, the result is unspecified. On some UNIX systems, select() unblocks and returns, with an indication that the file descriptor is ready (a subsequent I/O operation will likely fail with an error, unless another process reopens file descriptor between the time select() returned and the I/O operation is performed). On Linux (and some other systems), closing the file descriptor in another thread has no effect on select(). In summary, any application that relies on a particular behavior in this scenario must be considered buggy.

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 kernel_sigset_t *ss;   /* Pointer to signal set */
    size_t ss_len;               /* Size (in bytes) of object
                                    pointed to by 'ss' */
};


This allows the system call to obtain both a pointer to the signal set and its size, while allowing for the fact that most architectures support a maximum of 6 arguments to a system call. See sigprocmask(2) for a discussion of the difference between the kernel and libc notion of the signal set.

BŁĘDY

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).

The implementation of the fd_set arguments as value-result arguments means that they must be reinitialized on each call to select(). This design error is avoided by poll(2), which uses separate structure fields for the input and output of the call.

According to POSIX, select() should check all specified file descriptors in the three file descriptor sets, up to the limit nfds-1. However, the current implementation ignores any file descriptor in these sets that is greater than the maximum file descriptor number that the process currently has open. According to POSIX, any such file descriptor that is specified in one of the sets should result in the error EBADF.

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.

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 5.04 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 są: Przemek Borys <pborys@dione.ids.pl>, Robert Luberda <robert@debian.org> i Michał Kułach <michal.kulach@gmail.com>

Niniejsze tłumaczenie jest wolną dokumentacją. Bliższe informacje o warunkach licencji można uzyskać zapoznając się z GNU General Public License w wersji 3 lub nowszej. Nie przyjmuje się ŻADNEJ ODPOWIEDZIALNOŚCI.

Błędy w tłumaczeniu strony podręcznika prosimy zgłaszać na adres <manpages-pl-list@lists.sourceforge.net>.

19 listopada 2019 r. Linux