usleep für Millisekunden
-
Hallo.
Bin relativ neu in Linux und in C-Programmierung unter Linux.
Ich möchte mein Programm sleepen lassen.
In Windows ist es Sleep(millisekunden). In Linux gibt es sleep(sekunden), usleep(millisekunden), nanosleep(nanosekunden).
Das Programm soll 50 Millisekunden warten, bevor es fortfährt. usleep(50) bleibt komischerweise für 50 Sekunden stehen. Hä? Wieso?Bin dankbar für Hilfe.
Bis dennChris
-
[quote="ChrisK"]usleep(millisekunden)[/url]
Nein, da hast Du was verwechselt, usleep nimmt Mikrosekunden entgegen.Das Programm soll 50 Millisekunden warten, bevor es fortfährt. usleep(50) bleibt komischerweise für 50 Sekunden stehen. Hä? Wieso?
Das halte ich für ausgeschlossen. usleep kann zwar etwas ungenau sein, siehe dazu man: usleep(3):
DESCRIPTION The usleep() function suspends execution of the calling process for (at least) usec microseconds. [b]The sleep may be lengthened slightly by any system activity or by the time spent processing the call or by the granularity of system timers.[/b]
Aber von 50 Mikrosekunden zu 50 Sekunden wäre immerhin ein Faktor 1.000.000 zu überwinden, daran kann es also kaum liegen.
-
Ja, da hab ich wohl was verwechselt!
Ich hab's jetzt nochmal ausprobiert. Hab meinen Faktor den ich der usleep-Funktion übergeben habe einfach mit 1000 multipliziert, nun klappt es. Ich dachte aber, das hätte ich vorher schonmal probiert und das klappte nicht, aber das kann wohl nicht sein und ich hab mich auch beim Ausprobieren vertan...Auf jeden Fall Danke. Jetzt klappt alles
-
Es geht auch so (auf verschiedenen Unix-Derivaten)
int inputWait(msec) int msec; /* I: time to wait [msec] */ /* ret: <0 if any error */ /* procedure waits msec */ { struct timeval tv; /* help variable */ int errno2; /* help variable */ int ret; /* return variable */ ret = 0; if (msec >= 1000) { tv.tv_sec = msec / 1000; tv.tv_usec = (msec % 1000) * 1000; } else { tv.tv_sec = 0; tv.tv_usec = msec * 1000; } while (TRUE) { /* do looping while interrupt by signal catching */ if (select(0,NONE,NONE,NONE,&tv) < 0) { errno2 = errno; XBUG(DBG_SELECT) { fprintf(dbgfile,">>>inputWait(): INTR at select(), errno=%d\n",errno); fflush(dbgfile); } if (! ERRINTR(errno2)) break; /* else: EINTR => return looping */ } else break; } return(ret); }
Der Aufruf select() macht ein blockierendes Warten, bis die Zeitscheibe abgelaufen ist, also analog sleep().
Kann man natürlich auch für usec anpassen
-
1. Was bitte ist NONE, TRUE, XBUG, DBG_SELECT, ERRINTR, ....?!
2. select ändert timeout auf den meisten UNIX-Derivaten nicht um die verstrichene Zeit (laut Manpage)
3. Ist das 25 Jahre alte C-Syntax
-
Ich empfinde das jetzt mal nicht als Kritik, deshalb:
ad 3: ja (und wenn, darum geht es ja wohl nicht!)
ad 2: geht definitiv bei Linux (Red Hat, FC3), Reliant Unix, Solaris. Bei anderen Betriebsystemen kann es ggf. eine untere Begrenzung für die msec bzw. usec geben, aber wahrscheinlich wird das denn auch für andere Lösungen gelten.
ad 1: ... ist für Mitdenker gedacht, ist halt aus existierendem, lauffähigem Source-Code rauskopiert. Was hier von Interesse ist, einmal explizit:#define TRUE 1 #define NONE 0 (oder auch NULL) #define ERRINTR(e) ((e) == EINTR)
Wie bei vielen anderen low-level Systemaufrufen (wie z.B. bei read(), write()) müssen System-Interrupts explizit abgefangen werden. Dies gilt auch für select(). Dies geschieht hier mittels des Macros ERRINTR(), da neben EINTR bei anderen Betriebssystemen auch noch weitere Interrupts auftreten können, was größtenteils noch nicht einmal in den Manual-Pages beschrieben ist (Solaris ist hier ein schönes Beispiel). So können dies z.B. sein: ERESTART, ERESTARTSYS, ERESTARTNOINTR, ERESTARTNOHAND (ist halt Betriebssystem-abhängig ;-). Über das Macro wird eine Betriebssystem-unabhängige Behandlung realisiert.
-
Nur so als Anmerkung, waere ein
typedef enum boolean { TRUE = 1, FALSE = 0 // oder eben NONE } bool;
nicht besser als Konstanten zu definieren?
Wobei TRUE=1 auch nicht optimal!
-
zu 3) Hat zwar mit dem Problem nichts zu tun aber ich halte es trotzdem für unangebracht, Neulingen (die aus Fremdcode immerhin lernen) veraltete Syntax vorzusetzen. Und dieses Beispiel auf Aktuell zu trimmen wäre nicht unmöglich gewesen
zu 2) Wenn ich schon eine portable Lösung möchte, achte ich auf Portabilitätshinweise in Manual-Pages, und in meiner steht nunmal drin dass timeout nicht überall reduziert wird (so dass man sich nicht drauf verlassen kann dass der nächste select wirklich "weiterwartet")
zu 1) Auch hier denke ich Neulingen sollte man das Leben nicht durch Verwendung unbekannter Makros erschweren - es gibt schon genug Funktionen von denen einige Leute meinen sie gehörten zum Standard ("wieso gibt's conio.h bei mir nicht").
-
Zum Thema Kompatibilität:
Die zu der ursprünglichen Fragestellung "sleep mit Millisekunden bei Unix" vorgeschlagene Lösung mittels "usleep(millisekunden)" oder "nanosleep(nanosekunden)" ist definitiv nicht kompatibel, und vermutlich gibt es diese Aufrufe überwiegend bei Linux Systemen.
Nur mittels "select()" bzw. "poll()" kann man Unix/Linux-systemübergreifend im Bereich von Millisekunden warten.
Select() hat als Basis den Systemcall poll(). Hierzu steht in den Manual Pages von Sun:
If none of the defined events have occurred on any selected file descriptor, poll() waits at least timeout milliseconds for an event to occur on any of the selected file descrip- tors. On a computer where millisecond timing accuracy is not available, timeout is rounded up to the nearest legal value available on that system. If the value timeout is 0, poll() returns immediately. If the value of timeout is -1, poll() blocks until a requested event occurs or until the call is interrupted. The poll() function is not affected by the O_NDELAY and O_NONBLOCK flags.
Eine Formulierung, dass "timeout nicht überall reduziert wird" kann ich hier nicht erkennen.
Als Tipp: Welches die kleinste Zeiteinheit beim betreffenden System ist, kann man entweder
- mühsam herausfinden, wenn man alle Include's durchforstet
- oder mittels eines kleinen Testprogramms herausfinden (in dem man nämlich genau den select()-Aufruf in einer Schleife aufruft und anschließend mit der verbrauchten Ist-Zeit geschickt berechnet). Bis jetzt habe ich hier Werte zwischen 1 und 10 msec erhalten, abhängig vom verwendeten Unix-System. Werte unter 1 msec habe ich noch nicht gesehen.
-
jox schrieb:
Eine Formulierung, dass "timeout nicht überall reduziert wird" kann ich hier nicht erkennen.
Und aus dem nichtvorhandensein dieser Formulierung folgerst Du also, dass der Timeout reduziert wird? Davon steht hier nämlich ebenso wenig, und die meisten Referenzen sollten eigentlich den Anspruch haben so akkurat zu sein dass man nichts interpretieren muss.
* Upon successful completion, the select() function may modify the
object pointed to by the timeout argument.(Von meinem System) sagt mir auch nur dass die Funktion das Objekt ändern könnte und noch nicht einmal in welcher Weise.
-
Die Formulierung
* Upon successful completion, the select() function may modify the
object pointed to by the timeout argument.scheint mir nicht aus "offiziellen" Unix/Linux Manual-Pages zu stammen, da hier normalerweise nicht von "Objekten" gesprochen wird.
Nichtsdestotrotz findet man auf manchen Systemen (z.B. Linux) folgende ähnlich formulierte Anmerkung in den manual pages (Auszug):
On Linux, the function select modifies timeout to reflect the amount of time not slept; most other implementations do not do this. This causes problem both when Linux code which reads timeout is ported to other operating systems, and when code is ported to Linux that reuses a struct timeval for multiple selects in a loop without reinitializing it. Consider timeout to be undefined after select returns.
Wie gesagt, ist dieses Verhalten eine Eigenart von Linux-Systemen, bei der "Referenz" Sun oder anderen Systemen ist dies nicht so und wird folglich dort in den Manual-Pages auch nicht beschrieben.
Auf der sicheren Seite ist man (leider mit Ausnahmen) immer mit den Original Manual Pages des jeweiligen Betriebssystems. Die sind meiner Meinung nach auch häufig nicht einfach zu interpretieren, aber nach einigen Jahren Übung kommt man damit am besten zurecht. Der obige Manual-Auszug ist folgendermaßen zu interpretieren:
Bei manchen Systemen wird die Struktur "timeval" als zusätzlicher Rückgabewert benutzt (d.h. die Struktur wird nicht vor dem Aufruf bzw. vor Durchführung der eigentlichen Funktionalität vom Betriebssystem eigenständig modifiziert). Es wird hier die Zeit eingetragen, die nicht "verschlafen" wurde. Also z.B.: Soll 500 msec geschlafen werden, erfolgt aber eine Reaktivierung nach bereits 100 msec, so wird in "tv_usec" der Wert 400*1000 (=400 msec) eingetragen. Dies gilt aber nur, wenn der select() Aufruf vorzeitig zurückkommt. Das ist wiederum nur dann der Fall, wenn ein Interrupt erfolgt (z.B. EINTR), oder auf einem der beobachteten File-Descriptoren eine entsprechende Aktivität erfolgt ist.
Im Normalfall wird die select() Funktion nämlich zur Beobachtung von File-Descriptoren benutzt, doch dazu später mehr. In dem von mir angegebenen Beispiel mit
if (select(0,NULL,NULL,NULL,&tv) < 0) ...
wird select() als Spezialfall genutzt, nämlich ohne Beobachtung von File-Descriptoren. In diesem Fall wartet der Aufruf folglich bis zum Ablauf der spezifizierten Zeit. Ggf. kann also nur ein Interrupt, wie z.B. EINTR, das Schlafen vorzeitig unterbrechen. In diesem Fall wird in der "timeval" Struktur die verbliebene Zeit vermerkt. Da in meinem Beispiel dann die Funktion in der übergeordneten Zeit in der while-Schleife nochmals aufgerufen wird, wird auch noch für die verbliebene Zeit gewartet. (Anmerkung: diese Interrupts sind sehr, sehr selten und häufig nur bei produktivem Dauerbetrieb und großer Last zu beobachten).
Vor einem erneuten Aufruf der Funktion select() zur Ausrührung eines neuen sleep's ist auf jeden Fall die benutzte Struktur "timeval" komplett mit der Wartezeit zu belegen, um wieder mit einer definierten Wartezeit starten zu können (siehe letzten Satz aus dem Auszug der manual pages). Wird in dem Beispiel ebenfalls gemacht.Fazit:
Die Funktion "select()" in dem beschriebenen Spezialfall ist leider die einzige System-übergreifende und kompatible Lösung, um im Bereich vom Milli-Sekunden zu warten.I.d.R wird ein sleep im Millisekundenbereich eigentlich auch nicht wirklich benötigt, es sei denn, man will irgendwie auf ein Ereignis pollen. Diese Ereignisse sind dann häufig irgendwelche Eingaben auf Files. Genau dies kann man mit select() realisieren. Deswegen wird diese Funktion mit Vorliebe zur Programmierung von Server-Komponenten (z.B. Apache) genutzt.
Für weitere tiefgreifende Fragen zum Unix/Linux-Betriebssystem stehe ich gerne zur Verfügung.
-
Genauso wie Du es beschreibst habe ich den select-Aufruf verstanden und auch schon immer benutzt. Trotzdem bleibt die (inzwischen auch bestätigte) Tatsache dass timeval nicht immer mit der Restzeit gefüllt wird. Das sagst Du selbst:
Bei machen Systemen wird die Struktur "timeval" als zusätzlicher Rückgabewert benutzt [...] Dies gilt aber nur, wenn der select() Aufruf vorzeitig zurückkommt. Das ist wiederum nur dann der Fall, wenn ein Interrupt erfolgt (z.B. EINTR), oder auf einem der beobachteten File-Descriptoren eine entsprechende Aktivität erfolgt ist.
Das fett hervorgehobene Wort ist doch das, was ich im Ursprungsposting "bemängelt" habe.
Consider timeout to be undefined after select returns.
Das wird in dem Beispiel nicht gemacht (oder ich bin völlig blind), da timeval beim Folge-select unverändert weiterbenutzt wird.
-
Auf die Anmerkung habe ich gewartet. Situation Interrupt, z.B. EINTR:
Linux: Geänderte Struktur "timeval" (mit Rest-Wartezeit) wird im Beispiel in der While-Schleife für 2.ten Aufruf wiederbenutzt, funktioniert natürlich auch für den n-ten Aufruf.
Andere Unix-Systeme: Restzeit kann wie bei Linux nicht auf die beschriebene Art ermittelt werden. Struktur "timeval" ist unverändert. Select-Aufruf wird im Beispiel nochmals gestartet. Eventuell wartet man so jetzt zweimal die spezifizierte Zeit. Das ist zwar unschön, geht aber nicht anders. Man weiß nicht, ob der Interrupt zu Anfang oder am Ende der Wartezeit aufgetreten ist. Lieber zweimal warten als gar nicht, bei den meisten Anwendungen reicht so ein Verhalten völlig aus. Wie gesagt, so ein Interrupt ist äußerst selten.
Jetzt habe ich nicht verstanden, was bei welchem Unix-System nicht funktionieren soll.
P.S.: Auch bei den anderen sleep(), usleep(), nanosleep() Systemaufrufen hat man ein vergleichbares Verhalten, teilweise mit oder ohne Interrupt-Catching. I.d.R. kommt es auf die Anwendung an, eine exakte Wartezeit (z.B. zur Realisierung einer Uhr) ist mit diesen Funktionen nicht möglich.
-
Du widersprichst Dir:
Andere Unix-Systeme: Restzeit kann wie bei Linux nicht auf die beschriebene Art ermittelt werden. Struktur "timeval" ist unverändert. Select-Aufruf wird im Beispiel nochmals gestartet.
Vor einem erneuten Aufruf der Funktion select() zur Ausrührung eines neuen sleep's ist auf jeden Fall die benutzte Struktur "timeval" komplett mit der Wartezeit zu belegen, um wieder mit einer definierten Wartezeit starten zu können (siehe letzten Satz aus dem Auszug der manual pages). Wird in dem Beispiel ebenfalls gemacht.
"ist zu belegen" - verstehe ich im deutschen Sprachgebrauch als "musst Du belegen".
Versteh mich nicht falsch, ich verstehe was Dein Beispiel tut (wäre auch schlecht wenn nicht), aber in jedem Deiner Posts ist irgendeine Aussage widersprüchlich oder ungenau (oder liegt's an der deutschen Sprache?)
-
Ich glaube, wir reden aneinander vorbei. Die beiden Zitate sind aus unterschiedlichen Situationen entnommen:
Zitat zu Situation (1):
Andere Unix-Systeme: Restzeit kann wie bei Linux nicht auf die beschriebene Art ermittelt werden. Struktur "timeval" ist unverändert. Select-Aufruf wird im Beispiel nochmals gestartet.
Zitat zu Situation (2):
Vor einem erneuten Aufruf der Funktion select() zur Ausführung eines neuen sleep's ist auf jeden Fall die benutzte Struktur "timeval" komplett mit der Wartezeit zu belegen, um wieder mit einer definierten Wartezeit starten zu können (siehe letzten Satz aus dem Auszug der manual pages). Wird in dem Beispiel ebenfalls gemacht.
Situation (1): Interrupt, z.B. durch EINTR.
Situation (2): Völlig neuer Aufruf der Beispiel-Funktion "inputWait()". (Die Situation hätte man ggf. explizit erwähnen können
Die Manual Anmerkung "Consider timeout to be undefined after select returns" bedeutet, dass man z.B. bei wiederholten Aufrufen das Zeitintervall für die Wartezeit nicht in einem statischen Bereich ablegen soll und dann immer wieder unverändert nutzen kann, wenn man etwa eine Standardroutine fürs Warten von 100 msec implementieren will. Bei "normalen" Unix-Systemen könnte man einen statischen Bereich für die Struktur vorsehen, bei Linux geht das halt nicht.
Ich glaube, mein Beispiel-Code ist ein akzeptabler Kompromiss für die geforderte Funktionalität. Können wir uns darauf einigen?
-
jox schrieb:
Situation (2): Völlig neuer Aufruf der Beispiel-Funktion "inputWait()". (Die Situation hätte man ggf. explizit erwähnen können
Okay, ich hatte mich tatsächlich auf den erneuten Aufruf der select-Funktion innerhalb der while-Schleife versteift.
Ich glaube, mein Beispiel-Code ist ein akzeptabler Kompromiss für die geforderte Funktionalität. Können wir uns darauf einigen?
Jetzt ja