Gibt es eine Alternative für sleep() in C?

Lesezeit: 7 Minuten

Benutzer-Avatar
Manoj Zweifel

In der traditionellen eingebetteten Programmierung geben wir eine Verzögerungsfunktion wie folgt an:

for(i=0;i<255;i++)
   for(j=0;j<255;j++);

Funktioniert aus Sicht des Mikroprozessors die Funktion sleep() so?

Gibt es eine Alternative für die Funktion sleep() in C?

  • ok in meiner Umgebung ist Windows-Desktop-Maschine und Turbo-C-Compiler, dann gibt es für die Desktop-Umgebung eine Alternative für sleep ()? (nicht für eingebettete Systeme)

    – Manoj Zweifel

    5. November 2008 um 5:29 Uhr

  • Warum brauchen Sie eine Alternative zum Schlafen? Traditionell wird sleep so implementiert, dass keine Besetztschleife wie von Ihnen beschrieben ausgeführt werden muss. Stattdessen würde der Thread einfach nicht geplant werden.

    – Evan Teran

    5. November 2008 um 6:36 Uhr

Die Art von Schleife, die Sie beschreiben, wird als “Busy Wait” bezeichnet. In echten Betriebssystemen verursacht der Ruhezustand kein geschäftiges Warten; Es weist das Betriebssystem an, den Prozess nicht einzuplanen, bis die Ruhephase vorbei ist.

  • es gibt einen Platz für beide. Zum Beispiel ist der Windows-Kernel KeStallExecutionProcessor beschäftigt warten und KeDelayExecutionThread ist nicht beschäftigt warten. Es gibt eine Situation, in der Sie warten möchten, ohne den Kontext zu verlieren, zum Beispiel hw zurücksetzen -> paar Mikro warten -> Rest von hw init ausführen

    – Ilja

    5. November 2008 um 13:55 Uhr

  • Ich stimme zu, dass es Fälle gibt, in denen beschäftigtes Warten tatsächlich angemessen und im Kernel implementiert ist. zB Spin-Locking. Ich nehme an, meine Antwort sollte lauten: “In echten Betriebssystemen funktioniert das Schlafen nicht normalerweise langes Warten verursachen”. 🙂

    – C. K. Young

    6. November 2008 um 8:25 Uhr

Benutzer-Avatar
Adam Liss

Ein üblicher Mechanismus ist die Verwendung von a select() die garantiert abläuft, und geben Sie die Ruhezeit als Timeout an:

// Sleep for 1.5 sec
struct timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 500000;
select(0, NULL, NULL, NULL, &tv);

Das select() wird normalerweise verwendet, um eine Reihe von Dateideskriptoren zu überprüfen und zu warten, bis mindestens einer bereit ist, E/A auszuführen. Wenn keiner bereit ist (oder in diesem Fall, wenn keine fds angegeben sind), tritt eine Zeitüberschreitung auf.

Der Vorteil von select() gegenüber einer Besetztschleife ist, dass es im Ruhezustand sehr wenig Ressourcen verbraucht, während eine Besetztschleife den Prozessor so weit monopolisiert, wie es seine Prioritätsstufe zulässt.

  • Wahrscheinlich Over-Engineering, aber ich dachte, select() sei durch Signale unterbrechbar. Was passiert, wenn es nach 1/2 Sekunde unterbrochen wird?

    – paxdiablo

    5. November 2008 um 5:11 Uhr

  • Guter Fang – verwenden Sie in diesem Fall pselect(), um Signale zu maskieren, oder erstellen Sie Ihre eigenen schlafbewussten Signalhandler. Vielen Dank!

    – Adam Liss

    5. November 2008 um 5:22 Uhr

  • Aber es steht geschrieben hier (dort auf Waiting IO klicken) das select ist nicht unterbrechbar. Das Problem mit select wird hier erklärt

    – Abhishek Gupta

    27. Dezember 2012 um 10:21 Uhr


Alternativen hängen davon ab, was Sie versuchen zu tun und auf welchem ​​Betriebssystem Sie sich befinden.

Wenn Sie nur Zeit verschwenden wollen, könnten diese helfen:

Auf den meisten Unix-Systemen finden Sie eine ‘usleep’-Funktion, die mehr oder weniger wie sleep mit größerer Auflösung ist. Seien Sie vorsichtig mit diesem, da es normalerweise nicht für nur eine Mikrosekunde schlafen kann.

Auf einigen Unix-Systemen kann der Select-Systemaufruf mit allen Dateideskriptorsätzen auf Null verwendet werden, um eine ziemlich genaue Wartezeit von weniger als einer Sekunde zu erhalten.

Auf Windows-Systemen haben Sie Sleep, was ziemlich gleich ist, aber einige Millisekunden dauert.

In einem Multitasking-Betriebssystem kann einer Schlaffunktion manchmal 0 als Parameter gegeben werden. Dies führt im Allgemeinen dazu, dass die Funktion ihre Zeitscheibe aufgibt, aber sofort neu geplant wird, wenn keine andere Aufgabe zur Ausführung bereit ist.

Sie würden den von Ihnen veröffentlichten Code nicht verwenden, um auf einem eingebetteten System zu schlafen. Ein anständiger Compiler würde es vollständig entfernen, und selbst wenn Ihr Compiler es nicht entfernt, ist es suboptimal, da das Ausführen des Prozessors in einer engen Schleife Strom verbraucht, was ein Problem für eingebettete Systeme ist. Sogar Systeme, die nicht mit Batterie betrieben werden, achten auf den Stromverbrauch, da ein geringerer Stromverbrauch billigere Netzteile und Kühlung bedeutet.

Normalerweise implementiert Ihre CPU eine Art IDLE- oder SLEEP-Anweisungen, die dazu führen, dass sie die Verarbeitung von Befehlen vorübergehend stoppt. Eine externe Unterbrechungsleitung, die mit einer Zeitgeberschaltung verbunden ist, weckt den Prozessor in regelmäßigen Abständen wieder auf, und an diesem Punkt prüft die CPU, ob sie lange genug geschlafen hat, und wenn nicht, geht sie wieder in den Schlaf.

//Pseudo code
int start = getTime();
int end = start + sleepTime;

while (getTime() < end) {
       asm("SLEEP");
}

Die genauen Details variieren von Prozessor zu Prozessor. Wenn Sie als Prozess auf einem Betriebssystem ausgeführt werden, weist der Sleep-Aufruf im Allgemeinen nur den Scheduler an, Ihren Prozess anzuhalten, und dann entscheidet der Kernel, ob ein anderer Prozess geplant oder die CPU in den Ruhezustand versetzt werden soll. Außerdem ist der obige Code nicht für Echtzeitsysteme geeignet, die Termingarantien usw. wünschen. In diesen Fällen müssen Sie die Zeit in die Schleife bekommen, die Dauer des Zeitinterrupts kennen, damit Sie wissen, ob Sie ohne wieder schlafen können Überschreitung der Frist und möglicherweise Neuprogrammierung der Timer-Hardware oder geschäftiges Warten.

Sie sprechen im OP von “eingebetteter Programmierung”. Wenn Sie eingebettete Arbeiten ausführen und so etwas wie sleep() benötigen, stehen häufig Hardwarezähler/-timer zur Verfügung. Dies ist von Architektur zu Architektur unterschiedlich, also werfen Sie einen Blick auf das Datenblatt.

Wenn Sie keine eingebetteten Arbeiten ausführen, entschuldige ich mich 🙂

Wenn Sie for-Schleifen verwenden, sollten Sie besser wissen, was sie kompilieren und wie lange diese Anweisungen bei Ihrer gegebenen Taktgeschwindigkeit dauern. und Stellen Sie sicher, dass die CPU Ihre Anweisungen ausführt und nichts anderes (dies kann in eingebetteten Systemen durchgeführt werden, ist jedoch schwierig, da Interrupts nicht zugelassen werden).

Andernfalls können Sie nicht sagen, wie lange es wirklich dauern wird.

Frühe PC-Spiele hatten dieses Problem – sie wurden für einen 4,7-MHz-PC entwickelt und waren mit den schnelleren Computern unspielbar.

Der beste Weg, wie ein “Schlaf” funktionieren kann, besteht darin, dass die CPU weiß, wie spät es zu einem bestimmten Zeitpunkt ist. Nicht unbedingt die tatsächliche Zeit (7:15 Uhr), aber zumindest die relative Zeit (8612 Sekunden seit einem bestimmten Zeitpunkt).

Auf diese Weise kann es ein Delta auf die aktuelle Zeit anwenden und in einer Schleife warten, bis das aktuelle + Delta erreicht ist.

Alles, was auf einer Anzahl von CPU-Zyklen beruht, ist von Natur aus unzuverlässig, da die CPU möglicherweise zu einer anderen Aufgabe übergeht und Ihre Schleife hängen lässt.

Angenommen, Sie haben einen speicherabgebildeten 16-Bit-E/A-Port, den die CPU einmal pro Sekunde erhöht. Nehmen wir außerdem an, dass es sich in Ihrem eingebetteten System an Speicherort 0x33 befindet, wo Ints ebenfalls 16 Bit sind. Eine Funktion namens sleep wird dann zu etwas wie:

void sleep (unsigned int delay) {
    unsigned int target = peek(0x33) + delay;
    while (peek(0x33) != target);
}

Sie müssen sicherstellen, dass peek() jedes Mal den Speicherinhalt zurückgibt (damit optimierende Compiler die Logik nicht vermasseln) und dass Ihre while-Anweisung mehr als einmal pro Sekunde ausgeführt wird, damit Sie das Ziel nicht verfehlen, aber diese sind operative Probleme, die das von mir vorgestellte Konzept nicht beeinflussen.

Benutzer-Avatar
Gemeinschaft

Weitere Informationen zur Funktionsweise von sleep() finden Sie hier

Übrigens ist fleißiges Warten nicht unbedingt etwas für Amateure – obwohl es den Prozessor verbraucht, den Sie vielleicht für andere Zwecke verwenden möchten. Wenn Sie eine Zeitquelle verwenden, sind Sie auf die Granularität dieser Quelle beschränkt. ZB wenn Sie einen 1-ms-Timer haben und 500 uS weg möchten, haben Sie ein Problem. Wenn Ihr eingebettetes System damit umgehen kann, dass Sie 500 uSek lang in einer Schleife summen, ist dies möglicherweise akzeptabel. Und selbst wenn Sie einen Timer mit der gewünschten Granularität haben, müssen Sie auch einen Interrupt von diesem Timer zum richtigen Zeitpunkt bekommen … dann den Interrupt-Handler senden … und dann zu Ihrem Code gelangen. Manchmal ist eine Besetztschleife die sinnvollste Lösung. Manchmal.

1093930cookie-checkGibt es eine Alternative für sleep() in C?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy