FD_SET(2) synchrones

Other Alias

select, pselect, FD_CLR, FD_ISSET, FD_ZERO

ÜBERSICHT


/* laut POSIX.1-2001, POSIX.1-2008 */
#include <sys/select.h>



/* laut früherer 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);

Mit Glibc erforderliche Makros (siehe feature_test_macros(7)):

pselect(): _POSIX_C_SOURCE >= 200112L

BESCHREIBUNG

Mit den Funktionen select() und pselect() kann ein Programm mehrere Dateideskriptoren überwachen und warten, bis ein oder mehrere der Dateideskriptoren »bereit« für eine Klasse von E/A-Operationen sind (z. B. Eingabe möglich). Ein Dateideskriptor gilt als bereit, wenn es möglich ist, eine entsprechende E/A-Operation (z.B. read (2) ohne zu blockieren oder ein hinreichend kleines write(2)) durchzuführen.

select() can monitor only file descriptors numbers that are less than FD_SETSIZE; poll(2) does not have this limitation. See BUGS.

Das Verhalten von select() und pselect() ist bis die folgenden drei Unterschiede identisch:

(i)
select() verwendet für Zeitbegrenzungen (timeouts) eine struct timeval (mit Sekunden und Mikrosekunden), während pselect() stattdessen eine struct timespec (mit Sekunden und Nanosekunden) verwendet.
(ii)
Während select() das Argument timeout ändern darf, um die verbleibende Zeit anzugeben, verändert pselect() dieses Argument nicht.
(iii)
Die Funktion select() hat keinen Parameter sigmask und verhält sich wie pselect, wenn ihr für sigmask ein NULL-Zeiger übergeben wird.

Es werden drei voneinander unabhängige Mengen von Deskriptoren überwacht. Bei den in readfds enthaltenen wird darauf geachtet, ob neue Zeichen zum Lesen ankommen (genauer: ob eine Leseoperation nicht blockiert; insbesondere ist ein Dateideskriptor auch am Dateiende (EOF) bereit). Die in writefds angegebenen werden darauf überwacht, ob Platz zum Schreiben verfügbar ist (ein größerer Schreibaufruf kann aber dennoch blockieren) und die in exceptfds werden auf Ausnahmen (exceptions) überwacht. Wenn die Funktion beendet wird, werden die Mengen so verändert, dass sie anzeigen, welcher Deskriptor seinen Status geändert hat. Jeder der drei Mengen von Deskriptoren kann als NULL angegeben werden, falls für die entsprechenden Dateideskriptoren keine Klassen oder Ereignisse überwacht werden müssen.

Es werden vier Makros bereitgestellt, um mit diesen Mengen zu arbeiten. FD_ZERO() löscht eine Menge, FD_SET() fügt einen Deskriptor zu einer Menge hinzu und FD_CLR() löscht diesen. FD_ISSET() prüft, ob der Deskriptor in der Menge enthalten ist. Das ist insbesondere nach einem Aufruf von select() nützlich.

nfds entspricht der Nummer des am höchsten nummerierten Dateideskriptors in allen drei Mengen, plus 1.

Das Argument timeout legt das Intervall fest, das select() warten sollte, bis ein Dateideskriptor bereit wird. Der Aufruf wird blockieren, bis entweder:

*
ein Dateideskriptor bereit wird,
*
der Aufruf durch einen Signal-Handler unterbrochen wird, oder
*
die Wartezeit (timeout) abläuft.

Beachten Sie, das das Intervall timeout auf die Auflösung der Systemuhr aufgerundet wird. Durch Verzögerungen beim Kernel-Scheduling kann dieser Wert nochmals etwas größer werden. Falls beide Felder der Struktur timeval gleich null sind, kehrt select() sofort zurück. (Das ist praktisch für Polling). Falls timeout gleich NULL ist (kein timeout), kann select() auf unbestimmte Zeit blockieren.

sigmask ist ein Zeiger auf eine Signalmaske (siehe sigprocmask(2)); falls er ungleich NULL ist, ersetzt pselect() zuerst die aktuelle Signalmaske mit derjenigen, auf die sigmask weist, erledigt danach die »select«-Funktion und stellt als Letztes die ursprüngliche Signalmaske wieder her.

Abgesehen von der unterschiedlichen Genauigkeit des timeout-Arguments ist der pselect()-Aufruf

    ready = pselect(nfds, &readfds, &writefds, &exceptfds,
                    timeout, &sigmask);
äquivalent zur atomaren (unterbrechungsfreien, aufeinanderfolgenden) Ausführung der folgenden Aufrufe:
    sigset_t origmask;
    pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
    ready = select(nfds, &readfds, &writefds, &exceptfds, timeout);
    pthread_sigmask(SIG_SETMASK, &origmask, NULL);

Falls man auf die Verfügbarkeit eines Signals oder eines Dateideskriptors warten möchte, ist zur Vermeidung von Wettlaufsituationen (race conditions) eine atomare Prüfung erforderlich, die von pselect() erledigt wird. (Angenommen, der Signal Handler setzt ein globales Flag und kehrt zurück. Dann könnte eine Prüfung dieses globalen Flags gefolgt von einem Aufruf von select() auf unbestimmte Zeit hängen, wenn das Signal zwischen der Prüfung und vor dem Aufruf von select() eintrifft. Im Gegensatz dazu ermöglicht es pselect() zuerst Signale zu blockieren, die eingetroffenen Signale abzuarbeiten und anschließend pselect() mit der gewünschten sigmask aufzurufen, um Race Conditions zu vermeiden.)

Die Wartezeit (timeout)

Die Zeitstrukturen sind in <sys/time.h> als

struct timeval {
    long    tv_sec;         /* Sekunden */
    long    tv_usec;        /* Mikrosekunden */
};

und

struct timespec {
    long    tv_sec;         /* Sekunden */
    long    tv_nsec;        /* Nanosekunden */
};

definiert. (Sehen Sie sich aber weiter unten die Besonderheiten der POSIX.1-Versionen an.)

Es gibt Code, der select wie folgt aufruft: alle drei Deskriptor-Mengen leer, nfds gleich null und ein von NULL verschiedenes timeout als recht portabler Weg, um mit Auflösungen unterhalb einer Sekunde zu schlafen.

Unter Linux modifiziert select() timeout, um die nicht schlafend verbrachte Zeit anzuzeigen; die meisten anderen Implementierungen tun das nicht. (POSIX.1 lässt beiderlei Verhalten zu.) Dies führt zu Problemen sowohl bei der Portierung von Linux-Code, der timeout liest, auf andere Betriebssysteme als auch bei der Portierung von Code nach Linux, der eine struct timeval in einer Schleife für mehrfache Aufrufe von select() nutzt, ohne sie erneut zu initialisieren. Gehen Sie davon aus, dass timeout nach der Rückkehr aus select() nicht definiert ist.

RÜCKGABEWERT

Bei Erfolg geben select() und pselect() die Anzahl der Datei-Deskriptoren in den drei zurückgegebenen Deskriptor-Mengen zurück. (Das entspricht der Gesamtzahl von Bits, die in readfds, writefds und exceptfds gesetzt sind.) Der Wert kann null sein, falls die Wartezeit abläuft, ohne das irgendetwas von Bedeutung geschieht. Wenn ein Fehler auftritt, wird -1 zurückgegeben und errno gesetzt, um den Fehler anzugeben. Die Datei-Deskriptor-Mengen bleiben unverändert und timeout wird undefiniert.

FEHLER

EBADF
In einem der Mengen wurde ein ungültiger Dateideskriptor angegeben. (Vielleicht war es ein schon geschlossener Dateideskriptor oder einer, bei dem ein Fehler aufgetreten ist.)
EINTR
Ein Signal wurde abgefangen; siehe signal(7).
EINVAL
nfds ist negativ oder übersteigt die Ressourcenbegrenzung RLIMIT_NOFILE (siehe getrlimit(2)).
EINVAL
Der Wert von timeout ist ungültig.
ENOMEM
Speicher für interne Tabellen konnte nicht bereitgestellt werden.

VERSIONEN

pselect() wurde im Linux-Kernel 2.6.16 hinzugefügt. Vorher wurde pselect() in der Glibc emuliert (siehe aber FEHLER).

KONFORM ZU

select() conforms to POSIX.1-2001, POSIX.1-2008, and 4.4BSD (select() first appeared in 4.2BSD). Generally portable to/from non-BSD systems supporting clones of the BSD socket layer (including System V variants). However, note that the System V variant typically sets the timeout variable before exit, but the BSD variant does not.

pselect() ist in POSIX.1g und in POSIX.1-2001 und in POSIX.1-2008 definiert.

ANMERKUNGEN

An fd_set is a fixed size buffer. Executing FD_CLR() or FD_SET() with a value of fd that is negative or is equal to or larger than FD_SETSIZE will result in undefined behavior. Moreover, POSIX requires fd to be a valid file descriptor.

On some other UNIX systems, select() can fail with the error EAGAIN if the system fails to allocate kernel-internal resources, rather than ENOMEM as Linux does. POSIX specifies this error for poll(2), but not for select(). Portable programs may wish to check for EAGAIN and loop, just as with EINTR.

Concerning the types involved, the classical situation is that the two fields of a timeval structure are typed as long (as shown above), and the structure is defined in <sys/time.h>. The POSIX.1 situation is

struct timeval {
    time_t         tv_sec;     /* Sekunden */
    suseconds_t    tv_usec;    /* Mikrosekunden */
};

where the structure is defined in <sys/select.h> and the data types time_t and suseconds_t are defined in <sys/types.h>.

Concerning prototypes, the classical situation is that one should include <time.h> for select(). The POSIX.1 situation is that one should include <sys/select.h> for select() and pselect().

Under glibc 2.0, <sys/select.h> gives the wrong prototype for pselect(). Under glibc 2.1 to 2.2.1, it gives pselect() when _GNU_SOURCE is defined. Since glibc 2.2.2, the requirements are as shown in the SYNOPSIS.

Multithreaded applications

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 the file descriptor reopened between the time select() returned and the I/O operations was 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.

Unterschiede C-Bibliothek/Kernel

The Linux kernel allows file descriptor sets of arbitrary size, determining the length of the sets to be checked from the value of nfds. However, in the glibc implementation, the fd_set type is fixed in size. See also BUGS.

The pselect() interface described in this page is implemented by glibc. The underlying Linux system call is named pselect6(). This system call has somewhat different behavior from the glibc wrapper function.

The Linux pselect6() system call modifies its timeout argument. However, the glibc wrapper function hides this behavior by using a local variable for the timeout argument that is passed to the system call. Thus, the glibc pselect() function does not modify its timeout argument; this is the behavior required by POSIX.1-2001.

Das finale Argument des Systemaufrufs pselect6() ist kein sigset_t *-Zeiger, sondern eine Struktur der folgenden Form:

struct {
    const sigset_t *ss;     /* Zeiger auf Signalgruppe */
    size_t          ss_len; /* Größe (in Bytes) auf das Objekt, auf das
                               durch »ss« gezeigt wird */
};
Dies erlaubt es dem Systemaufruf, sowohl einen Zeiger auf die Signalgruppe als auch seine Größe zu ermitteln und dabei zu berücksichtigen, dass die meisten Architekturen eine maximale Anzahl von 6 Argumenten für einen Systemaufruf erlauben.

FEHLER

POSIX allows an implementation to define an upper limit, advertised via the constant FD_SETSIZE, on the range of file descriptors that can be specified in a file descriptor set. The Linux kernel imposes no fixed limit, but the glibc implementation makes fd_set a fixed-size type, with FD_SETSIZE defined as 1024, and the FD_*() macros operating according to that limit. To monitor file descriptors greater than 1023, use poll(2) instead.

Glibc 2.0 stellte eine Version von pselect() bereit, die das Argument sigmask nicht akzeptierte.

Starting with version 2.1, glibc provided an emulation of pselect() that was implemented using sigprocmask(2) and select(). This implementation remained vulnerable to the very race condition that pselect() was designed to prevent. Modern versions of glibc use the (race-free) pselect() system call on kernels where it is provided.

On systems that lack pselect(), reliable (and more portable) signal trapping can be achieved using the self-pipe trick. In this technique, a signal handler writes a byte to a pipe whose other end is monitored by select() in the main program. (To avoid possibly blocking when writing to a pipe that may be full or reading from a pipe that may be empty, nonblocking I/O is used when reading from and writing to the pipe.)

Under Linux, select() may report a socket file descriptor as "ready for reading", while nevertheless a subsequent read blocks. This could for example happen when data has arrived but upon examination has wrong checksum and is discarded. There may be other circumstances in which a file descriptor is spuriously reported as ready. Thus it may be safer to use O_NONBLOCK on sockets that should not block.

On Linux, select() also modifies timeout if the call is interrupted by a signal handler (i.e., the EINTR error return). This is not permitted by POSIX.1. The Linux pselect() system call has the same behavior, but the glibc wrapper hides this behavior by internally copying the timeout to a local variable and passing that variable to the system call.

BEISPIEL

#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;
    /* Beobachte stdin (fd 0), um zu sehen, wenn es Eingaben gibt. */
    FD_ZERO(&rfds);
    FD_SET(0, &rfds);
    /* Warte bis zu fünf Sekunden. */
    tv.tv_sec = 5;
    tv.tv_usec = 0;
    retval = select(1, &rfds, NULL, NULL, &tv);
    /* Verlassen Sie sich jetzt nicht auf den Wert von tv! */
    if (retval == -1)
        perror("select()");
    else if (retval)
        printf("Daten sind jetzt verfügbar.\n");
        /* FD_ISSET(0, &rfds) werden wahr sein. */
    else
        printf("Innerhalb von fünf Sekunden keine Daten.\n");
    exit(EXIT_SUCCESS);
}

KOLOPHON

Diese Seite ist Teil der Veröffentlichung 4.06 des Projekts Linux-man-pages. Eine Beschreibung des Projekts, Informationen, wie Fehler gemeldet werden können sowie die aktuelle Version dieser Seite finden sich unter https://www.kernel.org/doc/man-pages/.

ÜBERSETZUNG

Die deutsche Übersetzung dieser Handbuchseite wurde von Martin Schulze <[email protected]>, Daniel Kobras <[email protected]>, Martin Eberhard Schauer <[email protected]>, Helge Kreutzmann <[email protected]> und Mario Blättermann <[email protected]> erstellt.

Diese Übersetzung ist Freie Dokumentation; lesen Sie die GNU General Public License Version 3 oder neuer bezüglich der Copyright-Bedingungen. Es wird KEINE HAFTUNG übernommen.

Wenn Sie Fehler in der Übersetzung dieser Handbuchseite finden, schicken Sie bitte eine E-Mail an <[email protected]>.