Es gibt viele Schlaffunktionen in C-Sprache. Oft weiß ich nicht, wo ich was verwenden soll. Das heißt, Sie können kein echter C-Programmierer sein, wenn Sie die Schlaffunktion richtig wählen. C-Programmierer haben die Mission, das Beste aus vielen Optionen auszuwählen und eine konkurrenzlose Leistung zu liefern.
In diesem Artikel werde ich über den Unterschied zwischen usleep, nanosleep und clock_nanosleep schreiben, die eine hochpräzise Schlafverarbeitung realisieren können.
Die Zielversion des Linux-Kernels ist ** 5.4.2 **.
Nach diesem Abschnitt werde ich den Quellcode des Linux-Kernels lesen, aber das weiß ich nicht. Für diejenigen, die schnell eine Schlussfolgerung ziehen möchten, schreibe ich zuerst die Schlussfolgerung.
Wie, diese drei Funktionen machen *** fast das gleiche Verhalten ***!
Nur clock_nanosleep kann sein Verhalten abhängig vom Argument ändern. Wenn Sie jedoch CLOCK_MONOTONIC als erstes Argument von clock_nanosleep übergeben, ist das Verhalten der drei Funktionen dasselbe.
Jede dieser Funktionen hat jedoch eine andere Verwendbarkeit. Es scheint am besten, eine Funktion zu verwenden, die in jeder Situation einfach aufzurufen ist. Wenn Sie sicherstellen möchten, dass sie auf Quellcodeebene übereinstimmen, lesen Sie die folgenden Abschnitte.
Mal sehen, welche Funktionen usleep, nanosleep und clock_nanosleep sind. Diese Informationen stammen aus dem Linux-Programmierhandbuch des Menschen.
usleep usleep-Defer Ausführung in Mikrosekunden
#include <unistd.h>
int usleep(useconds_t usec);
nanosleep nanosleep-hochpräziser Schlaf
#include <time.h>
int nanosleep(const struct timespec *req, struct timespec *rem);
clock_nanosleep clock_nanosleep - Präziser Stopp (Schlaf) bei der angegebenen Uhr
#include <time.h>
int clock_nanosleep(clockid_t clock_id, int flags,
const struct timespec *request,
struct timespec *remain);
Nun, was ich tun möchte, ist fast dasselbe. Ich möchte im Nanosekundenbereich schlafen.
Als nächstes möchte ich jede Funktion nach Systemaufruf oder Bibliotheksfunktion klassifizieren. (Nun, Systemaufrufe bieten auch eine Schnittstelle auf der Bibliotheksseite, um den Assembler zu verpacken. Hier klassifizieren wir sie jedoch danach, ob sie als Linux-Kernel-Systemaufrufe definiert sind oder nicht.)
usleep | nanosleep | clock_nanosleep |
---|---|---|
Bibliotheksfunktion | Systemaufruf | Systemaufruf |
Es sieht aus wie das.
Betrachten wir nun jede Funktion auf Quellcode-Ebene.
usleep
Schauen wir uns nun die Definition von usleep an.
usleep ist eine von glibc definierte Funktion und kein Linux-Systemaufruf. Sie können dann erwarten, dass usleep einen Systemaufruf aufruft, um in dieser Funktion zu schlafen. Als ich mir tatsächlich den Quellcode von glibc angesehen habe, war es wie folgt.
https://github.com/lattera/glibc/blob/master/sysdeps/posix/usleep.c
int
usleep (useconds_t useconds)
{
struct timespec ts = { .tv_sec = (long int) (useconds / 1000000),
.tv_nsec = (long int) (useconds % 1000000) * 1000ul };
/* Note the usleep() is a cancellation point. But since we call
nanosleep() which itself is a cancellation point we do not have
to do anything here. */
return __nanosleep (&ts, NULL);
}
Wenn man sich diesen Quellcode und die Kommentare ansieht, scheint usleep den Aufruf des Nanosleep-Systems aufzurufen. Mit anderen Worten *** glibc "Ist es nicht mühsam, eine Zeitspezifikationsstruktur für das Argument von Nanosleep zu erstellen? Ich habe eine Wrapper-Funktion definiert." *** Es ist als solches definiert. Daher kann gesagt werden, dass usleep eine Funktion ist, die Mikrosekunden empfängt, eine darauf basierende Zeitspezifikationsstruktur erzeugt und schließlich den Nanosleep-Systemaufruf aufruft. Im Moment weiß ich, dass sich Usleep und Nanosleep ähnlich verhalten werden.
nanosleep Da nanosleep ein Linux-Systemaufruf ist, lesen wir den Quellcode des Linux-Kernels. nanosleep ist ein Systemaufruf /kernel/time/hrtimer.c line:1945 https://elixir.bootlin.com/linux/v5.4.2/source/kernel/time/hrtimer.c#L1945 ist.
SYSCALL_DEFINE2(nanosleep, struct __kernel_timespec __user *, rqtp,
struct __kernel_timespec __user *, rmtp)
{
struct timespec64 tu;
if (get_timespec64(&tu, rqtp))
return -EFAULT;
if (!timespec64_valid(&tu))
return -EINVAL;
current->restart_block.nanosleep.type = rmtp ? TT_NATIVE : TT_NONE;
current->restart_block.nanosleep.rmtp = rmtp;
return hrtimer_nanosleep(&tu, HRTIMER_MODE_REL, CLOCK_MONOTONIC);
}
Sie können endlich sehen, dass wir eine Funktion namens hrtimer_nanosleep aufrufen. Diese Funktion ist eine Funktion, die die Schlafverarbeitung mit einem hochpräzisen Timer namens High Resolution Timer ausführt. CLOCK_MONOTONIC wird an das dritte Argument der Funktion hrtimer_nanosleep übergeben. Oh, das hätte eine Konstante sein sollen, die auch an clock_nanosleep übergeben werden könnte. Nachdem ich etwas gerochen habe, werfen wir einen Blick auf die Implementierung des Systemaufrufs clock_nanosleep.
clock_nanosleep Lesen Sie abschließend den Quellcode von clock_nanosleep. Dieser Systemaufruf /kernel/time/posix-stubs.c line: 124 https://elixir.bootlin.com/linux/v5.4.2/source/kernel/time/posix-stubs.c#L124 Es ist definiert in.
SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags,
const struct __kernel_timespec __user *, rqtp,
struct __kernel_timespec __user *, rmtp)
{
struct timespec64 t;
switch (which_clock) {
case CLOCK_REALTIME:
case CLOCK_MONOTONIC:
case CLOCK_BOOTTIME:
break;
default:
return -EINVAL;
}
if (get_timespec64(&t, rqtp))
return -EFAULT;
if (!timespec64_valid(&t))
return -EINVAL;
if (flags & TIMER_ABSTIME)
rmtp = NULL;
current->restart_block.nanosleep.type = rmtp ? TT_NATIVE : TT_NONE;
current->restart_block.nanosleep.rmtp = rmtp;
return hrtimer_nanosleep(&t, flags & TIMER_ABSTIME ?
HRTIMER_MODE_ABS : HRTIMER_MODE_REL,
which_clock);
}
Diese Funktion überprüft zuerst die angegebene Uhr-ID. Abgesehen von CLOCK_REALTIME, CLOCK_MONOTONIC, CLOCK_BOOTTIME scheint ein Fehler aufzutreten. Nach dem Lesen von *** entspricht dies mit Ausnahme der Flags-Prüfung fast der Definition des Nanosleep-Systemaufrufs, den wir im vorherigen Abschnitt gesehen haben. *** ***
Die Beschreibung unterscheidet sich vorerst von Nanosleep
flags & TIMER_ABSTIME ?
HRTIMER_MODE_ABS : HRTIMER_MODE_REL
Ich werde diesen Teil auch kurz erklären. Flags sind Flags, die an den Systemaufruf clock_nanosleep übergeben werden können. Wenn dieses Flag auf *** TIMER_ABSTIME *** gesetzt ist, wird HRTIMER_MODE_ABS an die Funktion *** hrtimer_nanosleep *** übergeben, wenn dies nicht der Fall ist. Wenn gemäß der Beschreibung des Menschen das Flag *** TIMER_ABSTIME *** angegeben ist,
** Wenn flags TIMER_ABSTIME ist, wird das Argument rest nicht verwendet und wird nicht benötigt (das Anhalten bei einem absoluten Wert kann mit demselben Anforderungsargument erneut aufgerufen werden). ** ** **
ist das, was sie gesagt haben. Mit anderen Worten, ich habe festgestellt, dass dieser Prozess hinzugefügt wurde, um das Flag clock_nanosleep zu verarbeiten.
Ich habe die Schlussfolgerung oben gesagt, aber anscheinend verhalten sich diese drei Funktionen ähnlich. Mit anderen Worten, jede Methode ruft letztendlich die Funktion hrtimer_nanosleep auf, und der Prozess zum Bestimmen der Argumente, die an diese Funktion übergeben werden sollen, ist unterschiedlich. Wenn ich Sie zwinge, etwas zu verändern, denke ich, dass es diese Flexibilität ist. Obwohl usleep und nanosleep nur CLOCK_MONOTONIC als Sleep-Flag verwenden können, kann clock_nanosleep vom Programmierer frei entschieden werden.
Die Schlussfolgerung war, dass es am Ende keinen großen Unterschied gab, aber eine Open-Source-Kultur war wesentlich, um zu dieser Schlussfolgerung zu gelangen. Als bestes Open Source hoffe ich, dass sich freie Software weiterentwickelt!
Recommended Posts