Vorwort Um zum Originalcode kompatibel zu sein, bzw. gewisse Spezifikationen einzuhalten und den aktuell steigenden Anforderungen an die Genauigkeit gerecht zu werden, sind im Linux-Kernel eine Vielzahl zeitbezogener Datenstrukturen für unterschiedliche Zwecke implementiert: 1) jiffies und jiffies_64 Der Kernel verwendet die globale Variable jiffies_64, um aufzuzeichnen, wie viele Ticks seit dem Systemstart vergangen sind. Seine Deklaration lautet wie folgt (der Code befindet sich in kernel/time/timer.c): __visible u64 jiffies_64 __cacheline_aligned_in_smp = INITIAL_JIFFIES; EXPORT_SYMBOL(jiffies_64); Es ist ersichtlich, dass jiffies_64 als vorzeichenlose 64-Bit-Ganzzahl definiert ist. Aus historischen Gründen enthält der Kernel-Quellcode jedoch auch eine andere Variable namens „Jiffies“. Der Verweis auf Jiffies (der Code befindet sich in include/linux/jiffies.h) wird wie folgt deklariert: extern u64 __cacheline_aligned_in_smp jiffies_64; extern unsigned long volatile __cacheline_aligned_in_smp __jiffy_arch_data jiffies; Daher ist die Variable „Jiffies“ eine globale Variable vom Typ „Unsigned Long“, die auf einem 32-Bit-Prozessor nur 4 Byte (32 Bit) lang ist. Wenn der Prozessor jedoch 64 Bit lang ist, sind die globalen Variablen jiffies und jiffies_64 gleichwertig. Die Definition der globalen Variable lässt sich jedoch nach Durchforsten des gesamten Codes nicht finden. Schließlich findet man im Kernel-Link-Skript folgende Zeile (für die Arm64-Architektur befindet sich das Skript in arch/arm64/kernel/vmlinux.lds.S): jiffies = jiffies_64; Hier liegt das Geheimnis. Es stellt sich heraus, dass die Symbole jiffies und jiffies_64 so angegeben sind, dass sie beim Verknüpfen auf dieselbe Adresse verweisen. Das heißt, auf einem 32-Bit-Computer sind die unteren 4 Bytes von jiffies und jiffies_64 identisch. Im Allgemeinen können wir auf einem 32-Bit- oder einem 64-Bit-Computer direkt auf die globale Variable „jiffies“ zugreifen. Wenn wir jedoch die globale Variable „jiffies_64“ abrufen möchten, müssen wir die Funktion „get_jiffies_64“ aufrufen. Bei 64-Bit-Systemen sind die beiden identisch und Jiffies wird als volatil und cacheausgerichtet deklariert, geben Sie Jiffies also einfach direkt zurück: statisches Inline-u64 get_jiffies_64(void) { Rückkehr (u64)im Nu; } Da 64-Bit-Lese- und Schreibvorgänge bei 32-Bit-Systemen nicht atomar sind, ist außerdem die Lesereihenfolgesperre jiffies_lock erforderlich: u64 get_jiffies_64(ungültig) { vorzeichenlose Ganzzahlfolge; u64 zurück; Tun { : Seq = read_seqbegin(&jiffies_lock); ret = jiffies_64; } während (read_seqretry(&jiffies_lock, seq)); Rückkehr ret; } Grundsätzlich wird Jiffies bei jedem Eintreffen eines Ticks um 1 erhöht und die Tick-Periode HZ wird durch die Kernel-Kompilierungsoption konfiguriert. In einem 32-Bit-System gehen wir davon aus, dass HZ auf 250 eingestellt ist, dann beträgt die Periode jedes Ticks 4 Millisekunden und der Zähler läuft über, nachdem er den Maximalwert in weniger als 200 Tagen erreicht hat. Wird HZ höher eingestellt, ist die Überlaufzeit kürzer. Wenn Sie ein 64-Bit-System verwenden, müssen Sie sich um dieses Problem natürlich überhaupt keine Gedanken machen. Wenn Sie Jiffies für den Zeitvergleich verwenden, müssen Sie daher mehrere vom System definierte Makros verwenden: Zeit_nach(a,b) Zeit_vor(a,b) Zeit_nach_Gleichung(a,b) Zeit_vor_Gleichung(a,b) Zeit_im_Bereich_öffnen(a,b,c) Zeit_ist_vor_Augenblicken(a) Zeit_ist_nach_Augenblicken(a) Zeit_ist_vor_gleich_jiffies(a) Zeit_ist_nach_Gleichung_Jiffies(a) Um auf Nummer sicher zu gehen, stellt der Kernel auch eine entsprechende 64-Bit-Version bereit. Diese Makros können das Umbruchproblem wirksam lösen, sie sind jedoch nicht unbegrenzt. Wie wird das konkret umgesetzt? Schauen wir uns das time_after-Makro an: #definiere Zeit_nach(a,b) \ (Typprüfung (unsigned long, a) && \ Typprüfung (unsigned long, b) && \ ((lang)((b) - (a)) < 0)) Führen Sie zunächst eine Typprüfung der beiden Variablen durch. Beide müssen vom Typ „unsigned long“ sein. Das Wichtigste ist Folgendes: Subtrahieren Sie zuerst die beiden vorzeichenlosen langen Ganzzahlen, konvertieren Sie sie dann in vorzeichenbehaftete lange Ganzzahlen und bestimmen Sie dann, ob es sich um eine negative Zahl handelt, dh ob das höchste Bit der 32 Bits 1 ist. Warum kann dadurch das sogenannte Wraparound-Problem teilweise gelöst werden? Wir können der Einfachheit halber eine vorzeichenlose 8-Bit-Ganzzahl als Beispiel nehmen, ihr Wertebereich reicht von 0 bis 255 (0xFF). Angenommen, die aktuelle Zeit beträgt 250, dann beträgt sie nach 5 Ticks 255 und ist damit der maximal ausdrückbare Wert. Wenn zu diesem Zeitpunkt ein weiterer Tick vergeht, d. h. nach 6 Ticks, läuft er über und wird 0. Wenn Sie an dieser Stelle einfach die beiden Werte vergleichen, um zu bestimmen, welche Zeit später ist, ist dies offensichtlich falsch, da die Zeit nach 6 Ticks 0 ist, was kleiner als die aktuelle Zeit ist. Dieses Problem ist der sogenannte Wraparound. Wenn wir jedoch zuerst die beiden Zahlen subtrahieren, also 0-250 (0-0xFA), kommt es ebenfalls zu einem Überlauf und die Endzahl ist genau 6. Allerdings ist auch dies begrenzt, die Differenz der beiden verglichenen Zeiten kann nicht größer als die Hälfte des maximalen Darstellungsbereichs sein. Angenommen, die aktuelle Zeit beträgt immer noch 250 und nach 128 Ticks beträgt der Zeitwert 122. Wenn die beiden subtrahiert werden, ist das Ergebnis 122-250 (0x86-0xFA) und die subtrahierte Zahl ist 128. Wenn es zu diesem Zeitpunkt in eine vorzeichenbehaftete Zahl umgewandelt wird, wird es zu einer negativen Zahl und das Ergebnis ist falsch. Darüber hinaus werden Jiffies bei jedem Tick aktualisiert und die Tick-Periode wird zur Kompilierzeit definiert, sodass der Wert der Jiffies in die spezifische vergangene Zeitspanne umgerechnet werden kann und umgekehrt. Daher bietet der Kernel die folgenden Konvertierungsfunktionen: unsigned int jiffies_to_msecs(const unsigned long j); unsigned int jiffies_to_usecs(const unsigned long j); unsigned long ms_bis_jiffies(const unsigned int m); unsigned long usecs_to_jiffies(const unsigned int u); 2) timespec und timespec64 Timespec besteht aus Sekunden und Nanosekunden und ist wie folgt definiert (der Code befindet sich in include/uapi/linux/time.h): Struktur timespec { __kernel_time_t tv_sec; lange tv_nsec; }; tv_sec: Speichert die Anzahl der Sekunden, die seit 01. Januar 1970, 00:00 Uhr (UTC-Zeit) vergangen sind. __kernel_time_t ist letztendlich als langer Typ definiert, was bedeutet, dass es auf einem 32-Bit-System 32 Bit lang und auf einem 64-Bit-System 64 Bit lang ist. tv_nsec: speichert die Anzahl der Nanosekunden (ns), die seit der letzten Sekunde vergangen sind. Timespec verfügt außerdem über eine erweiterte 64-Bit-Struktur, die wie folgt definiert ist (der Code befindet sich in include/linux/time64.h): Typdefinition __s64 time64_t; ...... Struktur timespec64 { Zeit64_t tv_sec; lange tv_nsec; }; Die Variablendefinition in dieser Struktur ist dieselbe wie timespec, außer dass der Typ von tv_sec eine vorzeichenlose 64-Bit-Zahl sein muss. Auf 64-Bit-Systemen sind die Strukturen von timespec und timespec64 daher genau gleich. 3) ktime_t Im Linux-Zeitsubsystem wird zur Darstellung der Zeit im Allgemeinen ktime_t verwendet, das wie folgt definiert ist (der Code befindet sich in include/linux/ktime.h): Typdefinition s64 ktime_t; Es handelt sich um eine sehr einfache vorzeichenbehaftete 64-Bit-Ganzzahl, die die Zeiteinheit in Nanosekunden darstellt. 4) Zeitwert Die Funktionen gettimeofday und settimeofday verwenden timeval als Zeiteinheit: Struktur timeval { __kernel_time_t tv_sec; __kernel_suseconds_t tv_usec; }; tv_sec: Speichert die Anzahl der Sekunden, die seit 01. Januar 1970, 00:00 Uhr (UTC-Zeit) vergangen sind. __kernel_time_t ist letztendlich als langer Typ definiert, was bedeutet, dass es auf einem 32-Bit-System 32 Bit lang und auf einem 64-Bit-System 64 Bit lang ist. tv_usec:__kernel_suseconds_t ist tatsächlich als langer Typ definiert, der die Anzahl der Mikrosekunden (us) speichert, die seit der vorherigen Sekunde vergangen sind. Daher ist diese Struktur tatsächlich der timespec-Struktur sehr ähnlich. Der in tv_sec gespeicherte Wert ist derselbe, und Sie müssen nur tv_nsec in timespec durch 1000 teilen, um tv_usec in timeval zu erhalten. Zusammenfassen Dies ist das Ende dieses Artikels über die Zeitdarstellung des Linux-Zeitsubsystems. Weitere Informationen zur Linux-Zeitdarstellung finden Sie in früheren Artikeln auf 123WORDPRESS.COM oder in den folgenden verwandten Artikeln. Ich hoffe, Sie werden 123WORDPRESS.COM auch in Zukunft unterstützen! Das könnte Sie auch interessieren:
|
<<: Detaillierte Erläuterung von drei Beziehungsbeispielen für MySQL-Fremdschlüssel
>>: Ein Artikel zum Lösen des Echarts-Kartenkarussell-Highlights
So verwenden Sie CSS-Variablen in JS Verwenden Si...
Die Lösung für das Problem, dass Navicat keine Re...
1. Was ist HTML HTML (HyperText Markup Language):...
Installieren Sie die Zip-Dekomprimierungsfunktion...
HTML-Seitensprung: Fenster.öffnen(URL, "&quo...
Inhaltsverzeichnis I. Überblick 2. pt-archiver Ha...
In diesem Artikelbeispiel wird der spezifische Co...
Detaillierte Erklärung der Verwendung von DECIMAL...
--1. Erstellen Sie eine neue Gruppe und einen neu...
Bereits in den CSS2-Empfehlungen von 1998 verschwa...
Ich habe eine Produktteiletabelle wie diese: Teil...
Schauen wir uns zunächst die grundlegende Syntax ...
Vorwort Nachdem das Projekt auf .net Core migrier...
Der Nginx 502 Bad Gateway-Fehler ist mir schon me...