Der typische Weg zur Implementierung sleep()
und nanosleep()
besteht darin, das Argument in eine beliebige Skalierung des OS-Schedulers zu konvertieren (während der Abrundung) und die aktuelle Zeit hinzuzufügen, um eine "absolute Aufwachzeit" zu bilden; Dann sagen Sie dem Scheduler, dem Thread keine CPU-Zeit zu geben, bis die "absolute Aufwachzeit" erreicht ist. Kein beschäftigtes Warten ist beteiligt.
Beachten Sie, dass die Skalierung des OS-Schedulers normalerweise davon abhängt, welche Hardware verfügbar ist und/oder für die Zeitmessung verwendet wird. Sie kann kleiner als eine Nanosekunde sein (z. B. lokale APIC auf 80 · 86, die im "TSC-Terminierungsmodus" verwendet wird) oder so groß wie 100 ms.
Beachten Sie auch, dass das Betriebssystem garantiert, dass die Verzögerung nicht weniger als das ist, was Sie verlangen; aber es gibt normalerweise keine Garantie, dass es nicht länger sein wird und in einigen Fällen (z. B. Thread mit niedriger Priorität in einem stark ausgelasteten System) kann die Verzögerung viel viel größer als gewünscht sein. Wenn Sie zum Beispiel für 123 ns bitten zu schlafen, dann können Sie für 2 ms schlafen, bevor der Scheduler entscheidet, dass es Ihnen CPU-Zeit geben kann, und dann kann es weitere 500 ms geben, bevor der Scheduler Ihnen CPU-Zeit gibt (zB weil andere Threads verwenden die CPU).
Einige Betriebssysteme versuchen möglicherweise, dieses "länger geschlafen als angefordert" -Problem zu reduzieren, und einige Betriebssysteme (z. B. für hard-real-time ausgelegt) bieten möglicherweise eine Art Garantie (mit Einschränkungen - z. B. Thread-Priorität) Mindestzeit zwischen dem Ablauf der Verzögerung und dem Zurückholen der CPU. Um dies zu tun, würde das OS/kernel das Argument in jede Skalierung umwandeln, die der Scheduler des OS benutzt (während Abrundung und nicht Abrundung) und eine kleine Menge "nur für den Fall" subtrahieren kann; damit der Scheduler den Thread kurz vor Ablauf der angeforderten Verzögerung aufwacht (und nicht danach); und dann, wenn dem Thread CPU-Zeit gegeben wird (nachdem die Kosten des Kontextwechsels zum Thread und möglicherweise nach dem Vorabholen verschiedener Cache-Zeilen den Thread garantiert verwenden), würde der Kernel beschäftigt sein, kurz zu warten, bis die Verzögerung tatsächlich abgelaufen ist. Dies ermöglicht es dem Kernel, die Kontrolle wieder an den Thread zu übergeben, der dem Ablauf der Verzögerung extrem nahe kommt. Wenn Sie zum Beispiel für 123 ns bitten zu schlafen, gibt Ihnen der Scheduler möglicherweise keine CPU-Zeit für 100 ns, dann kann er 10 ns zu Ihrem Thread wechseln, dann wartet er möglicherweise auf die verbleibenden 13 ns. Selbst in diesem Fall (in dem das Besetztzeichen ausgeführt wird) wird normalerweise nicht auf die volle Dauer der Verzögerung gewartet.Wenn die Verzögerung jedoch extrem kurz ist, würde der Kernel nur das letzte busy-Warten durchführen.
Schließlich gibt es einen speziellen Fall, der erwähnenswert ist. Bei POSIX-Systemen wird sleep(0);
normalerweise als yield()
missbraucht. Ich bin nicht sicher, wie legitim diese Praxis ist - es ist unmöglich für einen Scheduler, etwas wie yield()
zu unterstützen, es sei denn, der Scheduler ist bereit, CPU-Zeit für unwichtige Arbeit zu verschwenden, während wichtigere Arbeit wartet.
Was ist los mit der Frage? – Kam
_ "Was ist los mit der Frage?" _ Zu breit. Betriebssystemspezifisch, implementierungsspezifisch. –
POSIX legt nicht fest, wie es implementiert werden soll – StenSoft