Hauptindex | Abschnitt 2 | English | 日本語 | Optionen |
int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
int pselect(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, sigset_t *sigmask);
FD_CLR(int fd, fd_set *set);
FD_ISSET(int fd, fd_set *set);
FD_SET(int fd, fd_set *set);
FD_ZERO(fd_set *set);
Die Funktionsweise der beiden Varianten ist identisch, abgesehen von drei Unterschieden:
(i) |
The select-Funktion verwendet zur Angabe von Zeitlimits eine Struktur
vom Typ struct timeval (mit Sekunden und Mikrosekunden), pselect
hingegen den Typ struct timespec (mit Sekunden und Nanosekunden).
Die Strukturen sind über /usr/include/sys/time.h definiert als:
struct timeval { long tv_sec; /* Sekunden */ long tv_usec; /* Mikrosekunden */ }; |
(ii) | Während select den Parameter timeout mitunter verändert, um anzuzeigen, wieviel Zeit des Limits noch verblieben ist, lässt pselect den Wert immer unverändert. |
(iii) | Die select-Funktion besitzt keinen sigmask-Parameter und verhält sich wie pselect, das mit einer sigmask von NULL aufgerufen wurde. |
Vier Makros stehen bereit, um mit diesen Mengen zu arbeiten. FD_ZERO löscht eine Menge, FD_SET und FD_CLR fügen einen Deskriptor zur Menge hinzu bzw. löschen diesen, FD_ISSET prüft, ob der Deskriptor in der Menge enthalten ist. Das ist insbesondere nach einem select- oder pselect-Aufruf sinnvoll.
n entspricht der Zahl des am höchsten nummerierten Datei-Deskriptors in allen drei Mengen, plus 1.
timeout gibt ein Zeitlimit an, das select und pselect maximal verstreichen lassen, bevor sie zurückkehren. Ist das Zeitlimit null, so kehren die Funktionen sofort zurück. Besitzt timeout selbst den Wert NULL, so wird kein Limit gesetzt, und die Funktionen können unendlich lange blockieren.
sigmask ist entweder NULL oder ein Zeiger auf eine Signalmaske wie in sigprocmask(2) beschrieben. Im zweiten Fall ersetzt pselect die aktuelle Signalmaske durch sigmask, führt dann den select-Aufruf aus und stellt anschließend die ursprüngliche Maske wieder her.
Die Idee hinter pselect geht zurück auf folgende Situation: Ein Programm wartet gleichzeitig auf ein Signal oder eine Veränderung an einem Datei-Deskriptor. Trifft das Signal ein, so setzt der Signalhandler eine globale Variable. Im Hauptprogramm wird zunächst getestet, ob die Variable gesetzt ist und andernfalls ein select-Aufruf gestartet. Trifft das Signal zwischen dem Test und dem select-Aufruf ein, so kann das dazu führen, dass das Programm nicht mehr beendet wird. pselect hingegen erlaubt es, diese sogenannte Race Condition zu umgehen, indem das Signal zunächst geblockt, getestet und erst unmittelbar mit dem select-Aufruf wieder freigegeben wird. Da der Linux-Kernel bislang keinen speziellen pselect-Systemaufruf bereit stellt, muss die aktuelle glibc2 ihn durch mehrere Aufrufe emulieren. Die Fehlerquelle ist daher auch mit pselect nach wie vor vorhanden.
EBADF | In einer der Mengen wurde ein ungültiger Datei-Deskriptor angegeben. |
EINTR | Ein nicht-blockiertes Signal wurde empfangen. |
EINVAL | n ist negativ. |
ENOMEM | select war nicht in der Lage, Speicher für die internen Tabellen zu bekommen. |
Bei Linux wird timeout derart verändert, dass es dem noch nicht verstrichenen Teil des Zeitlimits entspricht. Die meisten Implementierungen anderer Betriebssysteme unterlassen dies. Das bringt Probleme mit sich, wenn unter Linux geschriebener Quellcode, der timeout auswertet, auf andere Betriebssysteme portiert wird, und wenn Quellcode von anderen Betriebssystemen auf Linux portiert wird, der das struct timeval für mehrere selects in einer Schleife verwendet, ohne ihn jedesmal neu zu initialisieren. Portabler Code sollte daher annehmen, dass timeout undefiniert ist, nachdem select beendet wurde.
Wenn der einzige Schreiber eine Named-Pipe geschlossen hat, kehrt select zurück und signalisiert, dass etwas von der Pipe gelesen werden kann. Ein anschließender read-Aufruf liefert jedoch null zurück, da das Dateiende erreicht ist. Code, der annimmt, dass select in diesem Fall blockiert, sollte die Pipe mit O_RDWR statt O_RDONLY öffnen.
#include <stdio.h> #include <sys/time.h> #include <sys/types.h> #include <unistd.h>int main(void) { fd_set rfds; struct timeval tv; int retval;
/* Achte auf 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); /* Verlaß Dich jetzt bloß nicht auf den Wert von tv! */
if (retval) printf("Daten sind jetzt da. ); /* FD_ISSET(0, &rfds) müßte jetzt true sein. */ else printf("Keine Datein innerhalb von fünf Sekunden. );
exit(0); }
Die pselect-Funktion ist in IEEE Std 1003.1g-2000 (POSIX.1g) definiert. Sie ist seit glibc2.1 implementiert. Auch glibc2.0 besitzt eine Funktion dieses Namens, die jedoch keinen Parameter sigmask verwendet.
3. Dezember 2001 | SELECT (2) | Linux |
Hauptindex | Abschnitt 2 | English | 日本語 | Optionen |
Bitte richten Sie Ihre Kommentare zu diesem Handbuch Seite Service, Ben Bullock. Privacy policy.
“ | C isn't that hard: void (*(*f[])())() defines f as an array of unspecified size, of pointers to functions that return pointers to functions that return void | ” |