SUCHE MIT Google
Web virtualuniversity.ch
HOME DIDAKTIK ECDL ELEKTRONIK GUIDES HR MANAGEMENT MATHEMATIK SOFTWARE TELEKOM
DIENSTE
Anmeldung
Newsletter abonnieren
Sag's einem Freund!
VirtualUniversity als Startseite
Zu den Favoriten hinzufügen
Feedback Formular
e-Learning für Lehrer
Spenden
Autoren login
KURSE SUCHEN
Kurse veröffentlichen

Suche nach Datum:

Suche mit Schlüsselwort:

Suche nach Land:

Suche nach Kategorie:
PARTNER
ausbildung24.ch - Ausbildungsportal, Seminare, Kursen... 

 
HTMLopen.de - Alles was ein Webmaster braucht

 
PCopen.de - PC LAN Netze und Netzwerke - alles was ein IT Profi und Systemtechnicker braucht

TELEKOM

Ein-/Ausgabe-Polling mit select()

In Unix steht ein leistungsfähiger Mechanismus zur Verfügung,der es einem Anwenderprogramm ermöglicht, verschiedene Eingabekanäle"abzuhorchen", darüber festzustellen, ob mindestens einer davon Daten"anbietet" (oder zur Aufnahme von Daten bereit ist - s.u.), und im Anschlussdaran diesen zum Lesen bzw. Schreiben auszuwählen. Ein solches Szenarioist beispielsweise dann gegeben, wenn das Programm auf Daten wartet, dieihm ein anderes über eine Pipe liefern soll, und es gleichzeitig aufTastatureingaben reagieren möchte. In diesem Fall sind ja zwei Dateideskriptorenbetroffen: Der Deskriptor 0 für die Standardeingabe und der Eingabedeskriptor der Pipe. Schematisch könnte man dies wie folgt beschreiben:

Wiederhole:|Warte bis an der Standardeingabe ODER an der Pipe Daten anliegen||WENN Tastaturdaten vorliegen| lies diese ein| verarbeite sie||WENN an der Pipe Daten anliegen| lies diese ein| verarbeite siebis "fertig";Außerdem kann select() verwendet werden, wenn ein Server alseinzelner Prozeß mehrere Clients bedienen soll, da hier der Server erkennen kann, auf welchem Socket etwas gesendet oder empfangen werden soll. Dies ist notwendig, da der Aufruf von recv() so lange wartet, bis etwas empfangen wurde (er ist also blockierend). Der Server würde nun stehen bleiben, und das beim ersten Socket den er überprüft. Eine weitere Möglichkeit wäre nichtblockierende Ein-/Ausgabe (siehe fcntl()), die jedoch mehr Ressourcen braucht. select() wartet, bis etwas auf einem Socket aus der Socket-Liste ankommt bzw. gesendet werden kann. Nicht zuletzt kann man select() verwenden, um den Programmfluss für eine bestimmte Zeit zu unterbrechen (wie sleep() respektive usleep()). Zuerst die Deklaration von select() und dazugehöriger Makros:

#include <sys/time.h> #include <sys/types.h> #include <unistd.h> int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); FD_CLR(int fd, fd_set *set); FD_SET(int fd, fd_set *set); FD_ZERO(fd_set *set); FD_ISSET(int fd, fd_set *set); Die Parameter dieser Funktion haben die folgende Bedeutung:
  • n ist der höchste benötigte Datei-Deskriptor plus 1. Wenn der höchste relevante Deskriptor beispiesweise 7 ist, so muß das Bitmuster zu seiner Verwaltung acht Bits lang sei (0 bis 7) - n ist dann als 8 anzugeben.

  • readfds ist (wie auch writefds und exceptfds) ein Zeiger auf ein Bitmuster, bei dem jedes gesetzte Bit einer Deskriptornummer entspricht. Wenn beispielsweise in *readfds die Bits 0 und 5 gesetzt sind, so bedeutet dies, daß der aufrufende Prozeß warten möchte, bis entweder über den Dateideskriptor 0 oder über den Deskriptor 5 Daten gelesen werden können.

  • In *writefds bringt ein gesetztes Bit zum Ausdruck, daß gewartet werden soll, bis die entsprechende Datei zur Aufnahme von Daten bereit ist. Bei "normalen" Dateien geht das natürlich immer. Anders verhält es sich bei Deskriptoren, die auf eine Pipe oder auf einen Socket verweisen: Wenn der Sender seine Daten wesentlich schneller produziert, als sie vom Empfänger verarbeitet werden können, laufen die vom Betriebssystem bereitgestellte Puffer irgendwann voll. Der Sender muß nun warten, bis wieder Platz zur Verfügung steht.

  • *exceptfds nimmt Bits auf, die eine Fortsetzung des aufrufenden Prozesses dann veranlassen, wenn an den betreffenden Dateideskriptoren Fehlerzustände auftreten - in diesem Fall kann werder geschrieben noch gelesen werden, es besteht aber die Möglichkeit, auf die Fehlersituation geeignet zu reagieren.

  • timeout ist der Zeiger auf eine Datenstuktur, die einen Timeout vereinbart - also die Zeit, die select() verstreichen lassen soll bevor sie mit 0 zurückkehrt.
    Bei manchen Implementationen wird hier die Restzeit gespeichert wenn vor dem Ablaufen auf einem Deskriptor die geforderten Bedingungen zutreffen. Man sollte sich nicht darauf verlassen, jedoch ist es für portable Programme unbedingt notwendig, daß der Timeout vor einem erneuten Aufruf von select() wieder gesetzt wird, da er eventuell doch verändert worden sein kann. Die Struktur timeval ist in <sys/time.h> wie folgt deklariert:

    struct timeval { long tv_sec; /* seconds */ long tv_usec; /* microseconds */ }; Wenn die darin angegebene Zeit 0 ist (tv_sev == 0 und tv_usec == 0), blockiert select() überhaupt nicht, sondern kehrt sofort zurück. Wenn der Parameter "timeout" selbst gleich NULL ist (Nullzeiger), wartet select() unbegrenzt lange - bis sich auf einem der angesprochenen Dateideskriptoren etwas tut.

In der Manpage von select() unter Linux ist folgendes Beispiel angegeben:

#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; /* Watch stdin (fd 0) to see when it has input. */ FD_ZERO(&rfds); FD_SET(0, &rfds); /* Wait up to five seconds. */ tv.tv_sec = 5; tv.tv_usec = 0; retval = select(1, &rfds, NULL, NULL, &tv); /* Don't rely on the value of tv now! */ if (retval) printf("Data is available now.\n"); /* FD_ISSET(0, &rfds) == true */ else printf("No data within five seconds.\n"); exit(0); } Auch hier wird darauf hingewiesen, daß man nach dem Aufruf von select() nicht mehr auf den Wert in der Struktur timeval verlassen kann.

Nach Ausführung von select() enthalten auch die drei Bitmuster-Parameter nicht mehr die vor dem Aufruf gesetzten Bits, sondern es sind nur noch diejenigen gesetzt, deren zugeordnete Kanäle die Fortsetzung des Prozesses veranlasst haben.
Der Rückgabewert von select() enthält die Gesamtzahl der (noch) gesetzten Bits. Dieser Wert kann auch 0 sein, wenn der Timeout abgelaufen ist, ohne daß eine Verbindung eingegangen wurde. Bei einem Fehler wird -1 zurückgegeben.

Beispiel:

Bitnummer 7 6 5 4 3 2 1 0
Variable IBM: 0 0 0 1 0 0 0 1
Variable OBM: 1 0 0 0 0 0 0 0
Variable EBM: 1 0 0 0 0 0 0 0

Mit dem Aufruf

int i = select(8, &IBM, &OBM, &EBM, NULL); wird ohne Zeitbeschränkung darauf gewartet, daß entweder auf den "Kanälen" 0 oder 4 Eingabedaten zur Verfügung stehen, oder daß auf "Kanal" 7 ein Schreiben möglich ist (oder ein Fehler auftrat). Nach dem Verlassen der Funktion mit Rückgabewert 1 (nur noch 1 Bit gesetzt) sehen die Variablen wie folgt aus:

Bitnummer 7 6 5 4 3 2 1 0
Variable IBM: 0 0 0 0 0 0 0 1
Variable OBM: 0 0 0 0 0 0 0 0
Variable EBM: 0 0 0 0 0 0 0 0

Die C-Bibliothek stellt Makros zur Verfügung, die das Setzen, Löschen und Abfragen von Bits in den bei select() benutzten Bitmuster erleichtern. Die Deklaration ist oben schon aufgelistet. Hier einige Beispiele:

  • FD_SET(4,&IBM) setzt das Bit 4 im Bitmuster IBM
  • FD_CLR(2,&OBM) löscht das Bit 2 im Bitmuster OBM
  • FD_ISSET(4,&IBM) liefert "wahr", wenn Bit 4 in IBM gesetzt ist
  • FD_ZERO(&EBM) setzt EBM auf 0

Abschließend noch ein Beispiel: Ein Programm soll auf Eingaben von der Tastatur warten, aber alle 3 Sekunden den Benutzer zur Eingabe auffordern, wenn er nicht reagiert.

#include <unistd.h> #include <stdio.h> #include <sys/types.h> #include <sys/time.h> fd_set EBM; struct timeval Zeit; char buffer[1000]; int main() { do { printf("\nGibs mir:"); fflush(stdout); /* Ausgabepuffer leeren */ FD_ZERO(&EBM); /* Eingabebitmuster = 0 */ FD_SET(0,&EBM); /* Bit 0 setzen */ Zeit.tv_sec=3; /* Timeout = 3 Sekunden */ Zeit.tv_usec=0; } while (!select(1, &EBM, NULL, NULL, &Zeit)); /* Wenn select() mit 0 zurückkommt, ist die Uhr abgelaufen andernfalls steht eine Eingabe an */ fgets(buffer,1000,stdin); printf("Eingabe war: %s\n",buffer); } Der Aufruf fflush(stdout) wird in diesem Beispiel eingesetzt, damit die Eingabeaufforderung sofort auf dem Bildschirm erscheint.

Noch ein Beispiel: select() erlaubt es beispielsweise, ein Programm zu schreiben, das auf einem Port wartet und alle engehenden Daten an einen anderen Port weitergibt. Fertig ist der Proxy-Server! Angenommen man hat einen Rechner der per Modem eine Verbindung zum Internet aufgebaut hat als Gateway für ein lokales Netz dient (mittels IP-Masquerading). Nur der Gateway ist von aussen sichtbar, die Rechner des lokalen Netzes jedoch dahinter versteckt. Nehmen wir weiter an, daß auf einem der lokalen Rechner ein Web-Server läuft, der nach aussen Daten anbieten soll. Man braucht also ein Programm, das die Anfragen die an Port 80 des Gateway gelangen, zum Web-Server weitergereicht werden sollen (was einen einfachen Portforwarder aus dem Rennen wirft). Um das zu verwirklichen, braucht man also ein Programm das zwei Sockets geöffnet hat: einen zum Benutzer ausserhalb des Netzes, und einen zweiten der zum Web-Server führt. Das Programm muß erkennen, auf welchem Socket gerade etwas ankommt und diese Daten dann über den anderen Socket schicken. Eine Lösungsmöglichkeit wäre es, einen Firewall zu installieren. Mit select() geht es aber auch.

Das folgende Programmfragment zeigt, wie es geht.

int data_interchange(int src, int dest) { /* Implementierung der Polling-Methode + select() um * Systemressourcen zu sparen */ char buffer[BUFFER_SIZE]; int src_sent, src_recvd, dest_sent, dest_recvd, max, total, i; fd_set rfds; struct timeval tv; if (src > dest) max = src; else max = dest; total = 0; fcntl(src, F_SETFL, O_NONBLOCK); fcntl(dest, F_SETFL, O_NONBLOCK); for (;;) { FD_SET(src, &rfds); FD_SET(dest, &rfds); tv.tv_sec = 300; tv.tv_usec = 0; select(max + 1, &rfds, NULL, NULL, &tv); src_recvd = recv(src, buffer, sizeof(buffer), 0); dest_recvd = recv(dest, buffer, sizeof(buffer), 0); if (src_recvd > 0) send(dest, buffer, src_recvd, 0); if (dest_recvd > 0) send(src, buffer, dest_recvd, 0); if ((src_recvd == 0) || (dest_recvd == 0)) break; } return 0; } Wie man sieht, wartet select() darauf, daß von einem der beiden Sockets gelesen werden kann. Ist dies der Fall, wird gelesen. Man hätte auch mit FD_ISSET() testen können, von welchem Socket gelesen werden kann, doch so haben wir gleich noch ein Beispiel für nicht-blockierende Ein-/Ausgabe. Durch den Aufruf von fcntl() mit dem Attribut O_NONBLOCK blockiert ein recv()-Aufruf nicht, bis Daten eingetroffen sind, sondern kehrt sofort zurück. Die beiden if-Abfragen überprüfen, von welchem der Sockets eingetroffen sind (Wert > 0). Falls von keinem der beiden Sockets Daten kommen, ist ein Timeout aufgetreten. Das bedeutet, daß der Server-Prozeß nach fünf Minuten Inaktivität automatisch endet.

DIPLOMARBEITEN UND BÜCHER

Diplomarbeiten zum Runterladen:

Suche im Katalog:
Architektur / Raumplanung
Betriebswirtschaft - Funktional
Erziehungswissenschaften
Geowissenschaften
Geschichtswissenschaften
Informatik
Kulturwissenschaften
Medien- und Kommunikationswissenschaften
Medizin
Psychologie
Physik
Rechtswissenschaft
Soziale Arbeit
Sozialwissenschaften


JOBS
HOME | E-LEARNING | SITEMAP | LOGIN AUTOREN | SUPPORT | FAQ | KONTAKT | IMPRESSUM
Virtual University in: Italiano - Français - English - Español
VirtualUniversity, WEB-SET Interactive GmbH, www.web-set.com, 6301 Zug

Partner:   Seminare7.de - PCopen.de - HTMLopen.de - WEB-SET.com - YesMMS.com - Ausbildung24.ch - Manager24.ch - Job und Karriere