Scroll to navigation

SELECT(2) Linux-Programmierhandbuch SELECT(2)

BEZEICHNUNG

select, pselect, FD_CLR, FD_ISSET, FD_SET, FD_ZERO - synchrones E/A-Zeitmultiplexverfahren

ÜBERSICHT

#include <sys/select.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);
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 der Funktion select() kann ein Programm mehrere Dateideskriptoren überwachen und warten, bis ein oder mehrere der Dateideskriptoren »bereit« für eine Klasse von E/A-Aktionen sind (z. B. Eingabe möglich). Ein Dateideskriptor gilt als bereit, wenn es möglich ist, eine entsprechende E/A-Aktionen (z.B. read (2) oder ein hinreichend kleines write(2) ohne zu blockieren) durchzuführen.

select() kann nur Dateideskriptorennummern überwachen, die kleiner als FD_SETSIZE sind; poll(2) und epoll(7) haben diese Einschränkung nicht. Siehe FEHLER.

Dateideskriptor-Gruppen

Die hauptsächlichen Argumente von select() sind drei »Gruppen« von Dateideskriptoren (mit dem Typ fd_set deklariert), die es dem Aufrufenden erlauben, auf drei Ereignisklassen von der festgelegten Menge an Dateideskriptoren zu warten. Jedes der fd_set-Argumente kann als NULL angegeben werden, falls keine Dateideskriptoren für die entsprechende Ereignisklasse beobachtet werden soll.

Wohlgemerkt: Wenn die Funktion zurückkehrt, wird jede der Datei-Deskriptorenmengen so verändert, dass sie anzeigen, welcher Dateideskriptor derzeit »bereit« ist. Falls Sie daher select() innerhalb einer Schleife verwenden, müssen die Mengen vor jedem Aufruf neu initialisiert werden. Die Implementierung des Arguments fd_set als Wert-Ergebnis-Argument ist ein Design-Fehler, der in poll(2) und epoll(7) vermieden wurde.

Der Inhalt einer Dateideskriptor-Gruppe kann mit den folgenden Makros bearbeitet werden:

FD_ZERO()
Dieses Makro löscht (entfernt alle Dateideskriptoren aus) set. Es sollte als erster Schritt beim Initialisieren einer Dateideskriptor-Gruppe eingebunden werden.
FD_SET()
Dieses Makro fügt den Dateideskriptor fd zu set hinzu. Das Hinzufügen eines bereits in set vorhandenen Dateideskriptors ist ein Leerbefehl und löst keinen Fehler aus.
FD_CLR()
Dieses Makro entfernt den Dateideskriptor fd aus set. Das Hinzufügen eines nicht in set vorhandenen Dateideskriptors ist ein Leerbefehl und löst keinen Fehler aus.
FD_ISSET()
select() modifiziert den Inhalt der Gruppen entsprechend der nachfolgend bschriebenen Regeln. Nach dem Aufruf von select() kann mit dem Makro FD_ISSET() überprüft werden, ob ein Dateideskriptor noch in einer Gruppe vorhanden ist. FD_ISSET() gibt einen von 0 verschiedenen Wert zurück, falls der Dateideskriptor fd in set vorhanden ist; falls nicht, wird 0 zurückgegeben.

Argumente

Die Argumente von select() im Einzelnen:
readfds
Die Dateideskriptoren in dieser Gruppe werden überwacht, um festzustellen, ob sie bereit zum Lesen sind. Ein Dateideskriptor ist bereit zum Lesen, wenn ein Lesevorgang nicht blockiert; insbesondere ist ein Dateideskriptor auch bereit, wenn das Dateiende erreicht ist.
Nachdem select() zurückgekehrt ist, werden alle Dateideskriptoren aus readfds entfernt, außer jene, die bereit zum Lesen sind.
writefds
Die Dateideskriptoren in dieser Gruppe werden überwacht, um festzustellen, ob sie bereit zum Schreiben sind. Ein Dateideskriptor ist bereit zum Schreiben, wenn ein Schreibvorgang nicht blockiert. Dennoch könnte ein großer Schreibvorgang noch immer blockieren, selbst wenn ein Dateideskriptor als schreibbar angezeigt wird.
Nachdem select() zurückgekehrt ist, werden alle Dateideskriptoren aus writefds entfernt, außer jene, die bereit zum Schreiben sind.
exceptfds
Die Dateideskriptoren in dieser Gruppe werden auf »Ausnahmebedingungen« überwacht. Einige Beispiele für Ausnahmebedingungen finden Sie in den Erläuterungen zu POLLPRI in poll(2).
Nachdem select() zurückgekehrt ist, werden alle Dateideskriptoren aus exceptfds entfernt, außer jene, bei denen Ausnahmebedingungen aufgetreten sind.
nfds
Dieses Argument sollte auf die Nummer des am höchsten nummerierten Dateideskriptors in allen drei Gruppen plus 1 gesetzt werden. Der gekennzeichnete Dateideskriptor in jeder der drei Gruppen wird bis zu dieser Begrenzung geprüft (siehe aber FEHLER).
timeout
Das Argument timeout ist eine timeval-Struktur (nachfolgend beschrieben), welche das Intervall festlegt, 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 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 0 sind, kehrt select() sofort zurück. (Das ist praktisch für Polling).
Falls timeout als NULL festgelegt wurde, wird select() unendlich warten, bis ein Dateideskriptor bereit wird.

pselect()

Der Systemaufruf pselect() ermöglicht einer Anwendung, sicher zu warten, bis entweder ein Dateideskriptor bereit wird oder bis ein Signal empfangen wird.

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

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

sigmask ist ein Zeiger auf eine Signalmaske (siehe sigprocmask(2)); falls er ungleich NULL ist, ersetzt pselect() zuerst die aktuelle Signalmaske durch diejenige, auf die sigmask weist, erledigt danach die »select«-Funktion und stellt als Letztes die ursprüngliche Signalmaske wieder her. (Falls sigmask nicht NULL ist, wird die Signalmaske während des pselect()-Aufrufs nicht verändert.)

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


ready = pselect(nfds, &readfds, &writefds, &exceptfds,
                timeout, &sigmask);


ist äquivalent zur atomaren 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 einen globalen Schalter und kehrt zurück. Dann könnte eine Prüfung dieses globalen Schalters 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)

Das Argument timeout für select() ist eine Struktur des folgenden Typs:


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


Das korrespondierende Argument für pselect() hat den folgenden Typ:


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


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-Gruppen zurück. (Das entspricht der Gesamtzahl von Bits, die in readfds, writefds und exceptfds gesetzt sind.) Der Rückgabewert kann 0 sein, falls die Wartezeit ablief, bevor der Dateideskriptor bereitstand.

Bei einem Fehler wird -1 zurückgegeben und errno wird gesetzt, um den Fehler anzuzeigen; die Dateideskriptor-Gruppen werden nicht verändert und timeout wird undefiniert.

FEHLER

EBADF
In einer der Gruppen wurde ein ungültiger Dateideskriptor angegeben. (Vielleicht war es ein schon geschlossener Dateideskriptor oder einer, bei dem ein Fehler aufgetreten ist.) Lesen Sie aber auch FEHLER.
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() erfüllt POSIX.1-2001, POSIX.1-2008 und 4.4BSD (select() erschien erstmalig in 4.2BSD). Im Allgemeinen von/nach nicht BSD-Systeme portabel, unterstützt Klone der BSD-Socket-Schicht (einschließlich System-V-Varianten). Beachten Sie allerdings, dass System-V-Varianten typischerweise die Variable »timeout« vor dem Zurückkehren setzen, die BSD-Variante aber nicht.

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

ANMERKUNGEN

Ein fd_set ist ein Puffer fester Größe. Wird FD_CLR() oder FD_SET() mit einem Wert für fd, der negativ, gleich groß oder größer als FD_SETSIZE ist, ausgeführt, führt dies zu nicht definiertem Verhalten. Desweiteren verlangt POSIX, dass fd ein gültiger Dateideskriptor ist.

Das Verhalten von select() und pselect() ist von dem Schalter O_NONBLOCK nicht betroffen.

Unter einigen UNIX-Systemen kann select() mit dem Fehler EAGAIN fehlschlagen, falls es dem System nicht gelingt, Kernel-interne Ressourcen zuzuweisen. Linux verwendet hierbei ENOMEM. POSIX legt diesen Fehler für poll(2) aber nicht für select() fest. Portable Anwendungen könnten in einer Schleife auf EAGAIN (wie auch auf EINTR) prüfen.

Der Selbst-Pipe-Trick

Auf Systemen, auf denen pselect() fehlt, kann ein zuverlässiges (und portableres) Abfangen von Signalen mit dem Selbst-Pipe-Trick erreicht werden. Bei dieser Technik schreibt ein Signal-Handler ein Byte in eine Pipe, dessen anderes Ende von select() im Hauptprogramm überwacht wird. (Um mögliches Blockieren beim Schreiben in eine Pipe, die voll sein könnte, oder Lesen aus einer Pipe, die leer sein könnte, zu vermeiden, wird nicht blockierende E/A beim Auslesen und Schreiben in die Pipe verwandt.)

Emulieren von usleep(3)

Vor dem Aufkommen von usleep(3) gab es Code, der select wie folgt aufrief: alle drei Deskriptor-Gruppen leer, nfds gleich 0 und ein von NULL verschiedenes timeout als recht portabler Weg, um mit Auflösungen unterhalb einer Sekunde zu schlafen.

Korrespondenz zwischen den Benachrichtigungen select() und poll()

Innerhalb der Linux-Kernelquellen finden wir die folgenden Definitionen, die die Korrespondenz zwischen den lesbaren, schreibbaren und außerordentlichen Zustandsbenachrichtigungen von select() und den durch poll(2) (und epoll(7)) bereitgestellten Ereignisbenachrichtigungen zeigt:


#define POLLIN_SET  (EPOLLRDNORM | EPOLLRDBAND | EPOLLIN |
                     EPOLLHUP | EPOLLERR)
                   /* Bereit zum Lesen */
#define POLLOUT_SET (EPOLLWRBAND | EPOLLWRNORM | EPOLLOUT |
                     EPOLLERR)
                   /* Bereit zum Schreiben */
#define POLLEX_SET  (EPOLLPRI)
                   /* Außergewöhnliche Bedingung */


Multithreaded Anwendungen

Falls ein Dateideskriptor, der durch select() überwacht wird, in einem anderen Thread geschlossen wird, ist das Ergebnis nicht spezifiziert. Auf einigen UNIX-Systemen entblockt select() und kehrt mit einer Information zurück, dass der Dateideskriptor bereit ist (eine nachfolgende E/A-Aktion wird wahrscheinlich mit einem Fehler fehlschlagen, außer ein anderer Prozess hat den Dateideskriptor zwischen dem Zeitpunkt, zu dem select() zurückkehrte, und dem Zeitpunkt, zu der die E/A-Aktion stattfindet, wieder geöffnet). Unter Linux (und einigen anderen Systemen) hat das Schließen des Dateideskriptors in einem anderen Thread keinen Effekt auf select(). Zusammenfassend sollte jede Anwendung, die sich auf ein bestimmtes Verhalten in diesem Szenario abstützt, als fehlerhaft betrachtet werden.

Unterschiede C-Bibliothek/Kernel

Der Linux-Kernel erlaubt Dateideskriptor-Gruppen beliebiger Größe und bestimmt die Größe der zu prüfenden Gruppen durch den Wert von nfds. In der Glibc-Implementierung ist der Typ fd_set allerdings von fester Größe. Siehe auch FEHLER.

Die in dieser Seite beschriebene Schnittstelle von pselect() wird durch Glibc implementiert. Der darunterliegende Systemaufruf heißt pselect6(). Dieser Systemaufruf hat ein etwas anderes Verhalten als die Glibc-Wrapper-Funktion.

Der Systemaufruf pselect6() von Linux verändert das Argument timeout. Die Glibc-Wrapper-Funktion versteckt dieses Verhalten durch Verwendung einer lokalen Variablen für das Argument »timeout«, die an den Systemaufruf übergeben wird. Daher verändert die Glibc-Funktion pselect() nicht ihr Argument timeout; dies ist das von POSIX.1-2001 verlangte Verhalten.

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


struct {
    const kernel_sigset_t *ss;  /* Zeiger auf Signalgruppe */
    size_t ss_len;              /* Größe (in Bytes) des Objekts,
                                   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. Siehe sigprocmask(2) für eine Diskussion der Unterschiede zwischen der Ansicht des Kernels und der Ansicht der Libc bezüglich der Singalmenge.

Geschichtliche Glibc-Details

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

In den Glibc-Versionen von 2.1 bis 2.2.1 musste _GNU_SOURCE definiert werden, um die Deklaration von pselect() aus <sys/select.h> zu erhalten.

FEHLER

POSIX erlaubt einer Implementierung, eine oberer Begrenzung für den Bereich von Dateideskriptoren, die in einer Dateideskriptor-Gruppe festgelegt werden können, zu definieren. Diese Begrenzung soll mittels der Konstanten FD_SETSIZE bekannt gemacht werden. Der Linux-Kernel erzwingt keine feste Begrenzung, aber die Glibc-Implementierung macht fd_set zu einem Typ fester Größe, wobei FD_SETSIZE als 1024 definiert ist und die Makros FD_*() arbeiten entsprechend dieser Begrenzung. Um Dateideskriptoren größer als 1024 zu überwachen, verwenden Sie stattdessen poll(2) oder epoll(7).

Laut POSIX sollte select() alle festgelegten Dateideskriptoren in den drei Dateideskriptor-Gruppen bis zur Grenze nfds-1 prüfen. Allerdings ignoriert die aktuelle Implementierung jeden Dateideskriptor in diesen Gruppen, der größer als die maximale Dateideskriptorennummer ist, die der Prozess derzeit offen hat. Laut POSIX sollte jeder solcher Dateideskriptoren, der in einer der drei Gruppen festgelegt ist, zu einem Fehler EBADF führen.

Beginnend mit Version 2.1 stellt Glibc eine Emulation von pselect() bereit, die mittels sigprocmask(2) und select() implementiert wurde. Diese Implementierung blieb für den starken Ressourcenwettlauf verwundbar, der durch das Design von pselect() vermieden werden sollte. Moderne Versionen der Glibc verwenden den (ressourcenwettlauffreien) pselect()-Systemaufruf auf Kerneln, die ihn bereitstellen.

Unter Linux könnte select() einen Socket-Dateideskriptor als »bereit zum Lesen« melden, obwohl trotzdem ein folgendes Lesen blockiert. Dies könnte beispielsweise passieren, wenn Daten angekommen sind, aber bei genauerer Prüfung die falsche Prüfsumme haben und daher verworfen werden. Es mag andere Umstände geben, in denen ein Dateideskriptor fälschlicherweise als bereit berichtet werden könnte. Daher mag es sicherer sein, O_NONBLOCK bei Sockets zu benutzen, die nicht blockieren sollen.

Unter Linux verändert select() auch timeout falls der Aufruf durch ein Signal-Handler unterbrochen wird (d.h. den Fehler EINTR zurückliefert). Dies wird von POSIX.1 nicht erlaubt. Der Linux-Systemaufruf pselect() zeigt das gleiche Verhalten, aber der Glibc-Wrapper versteckt das Verhalten, indem er intern timeout in eine lokale Variable kopiert und diese Variable an den Systemaufruf übergibt.

BEISPIELE

#include <stdio.h>
#include <stdlib.h>
#include <sys/select.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);
}

SIEHE AUCH

accept(2), connect(2), poll(2), read(2), recv(2), restart_syscall(2), send(2), sigprocmask(2), write(2), epoll(7), time(7)

Für eine Anleitung mit Diskussionen und Beispielen lesen Sie select_tut(2).

KOLOPHON

Diese Seite ist Teil der Veröffentlichung 5.07 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 <joey@infodrom.org>, Daniel Kobras <kobras@linux.de>, Martin Eberhard Schauer <Martin.E.Schauer@gmx.de>, Mario Blättermann <mario.blaettermann@gmail.com> und Helge Kreutzmann <debian@helgefjell.de> 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 <debian-l10n-german@lists.debian.org>.

11. April 2020 Linux