/****************** * Zeitverwaltung des Linux-Kernels *********************/ (1) Konzept der Zeit im Kernel Zeitmanagement spielt im Linux-Kernel eine sehr wichtige Rolle. Im Vergleich zu ereignisgesteuerten Funktionen gibt es im Kernel eine große Anzahl zeitgesteuerter Funktionen. Einige Funktionen werden periodisch ausgeführt, wie beispielsweise das Aktualisieren des Bildschirms alle 10 Millisekunden. Einige Funktionen werden nach einer bestimmten Zeit ausgeführt, beispielsweise führt der Kernel eine Aufgabe nach 500 Millisekunden aus. Zur Unterscheidung:
Periodische Ereignisse werden vom Systemtimer gesteuert (2)HZ-Wert Der Kernel benötigt zur Berechnung und Verwaltung der Zeit die Hilfe von Hardware-Timern. Die Häufigkeit, mit der ein Timer Interrupts generiert, wird als Tickrate bezeichnet. Im Kernel wird eine Variable HZ angegeben und die Taktfrequenz des Timers wird bei der Initialisierung des Kernels auf Grundlage dieses Wertes bestimmt. HZ ist in <asm/param.h> definiert. Auf der i386-Plattform beträgt der aktuell verwendete HZ-Wert 1000. Das heißt, die Taktunterbrechung erfolgt 1000 Mal pro Sekunde mit einer Periode von 1 Millisekunde. Im Augenblick: Beachten! HZ ist kein fester Wert, er kann geändert und bei der Konfiguration des Kernel-Quellcodes eingegeben werden. Verschiedene Architekturen haben unterschiedliche HZ-Werte, Arm verwendet beispielsweise 100. Wenn Sie die Systeminterruptfrequenz im Treiber verwenden möchten, verwenden Sie HZ direkt anstelle von 100 oder 1000 a. Idealer HZ-Wert Der HZ-Wert von i386 war immer 100, bis er nach Version 2.5 auf 1000 geändert wurde. Eine Erhöhung der Tick-Rate bedeutet, dass Takt-Interrupts häufiger generiert und Interrupt-Handler häufiger ausgeführt werden. Die Vorteile sind:
(Gekürzte Planungsverzögerung: Wenn dem Prozess in einem 10-ms-Planungszyklus noch ein Zeitfenster von 2 ms verbleibt, läuft der Prozess 8 ms länger. Die Nachteile sind: *Je höher die Schlagfrequenz, desto höher die Systembelastung. Interrupt-Handler beanspruchen mehr Prozessorzeit. (3) Augenblicke Mit der globalen Variable „jiffies“ wird die Gesamtzahl der seit dem Systemstart generierten Beats aufgezeichnet. Beim Start wird jiffies auf 0 initialisiert und danach jedes Mal durch den Takt-Interrupt-Handler erhöht. Auf diese Weise beträgt die Laufzeit nach dem Start des Systems Jiffies/HZ Sekunden Jiffies sind in <linux/jiffies.h> definiert: Die Variable „Jiffies“ ist immer vom Typ „unsigned long“. Bei einer 32-Bit-Architektur sind es also 32 Bit und bei einer 64-Bit-Architektur 64 Bit. Bei 32-Bit-Jiffies kommt es bei einem HZ-Wert von 1000 nach 49,7 Tagen zu einem Überlauf. Obwohl ein Überlauf selten vorkommt, ist es dennoch möglich, dass ein Programm beim Erkennen einer Zeitüberschreitung Fehler aufgrund von Umbruch verursacht. Linux stellt vier Makros zum Vergleichen der Beat-Zählung bereit, die den Beat-Count-Wraparound korrekt handhaben können. #include <linux/jiffies.h> #define time_after(unbekannt, bekannt) // unbekannt > bekannt #define time_before(unbekannt, bekannt) // unbekannt < bekannt #define time_after_eq(unbekannt, bekannt) // unbekannt >= bekannt #define time_before_eq(unbekannt, bekannt) // unbekannt <= bekannt Unbekannt bezieht sich normalerweise auf Jiffies, und bekannt ist der zu vergleichende Wert (normalerweise ein relativer Wert, der durch Addieren oder Subtrahieren von Jiffies berechnet wird). Beispiel: unsigned long timeout = jiffies + HZ/2; /* Timeout nach 0,5 Sekunden */ ... wenn (Zeit_vor(jiffies, Timeout)) { /* Kein Timeout, gut */ }anders{ /* Zeitüberschreitung, Fehler aufgetreten */ time_before kann so verstanden werden, als ob es vor dem Timeout (vorher) abgeschlossen wäre. *Im System wird auch ein 64-Bit-Wert jiffies_64 deklariert. In einem 64-Bit-System haben jiffies_64 und jiffies denselben Wert. Dieser Wert kann über get_jiffies_64() abgerufen werden. *verwenden u64 j2; j2 = get_jiffies_64(); (4) Aktuelle Uhrzeit abrufen Fahrer müssen die aktuelle Uhrzeit (also die Uhrzeit des Jahres, des Monats und des Tages) im Allgemeinen nicht kennen. Der Fahrer muss sich jedoch möglicherweise mit absoluten Zeiten auseinandersetzen. Struktur timeval { time_t tv_sec; /* Sekunden */ suseconds_t tv_usec; /* Mikrosekunden */ }; // Älter, aber beliebt. Es speichert in Sekunden und Millisekunden die Anzahl der Sekunden seit 0:00 am 1. Januar 1970. struct timespec { time_t tv_sec; /* Sekunden */ long tv_nsec; /* Nanosekunden */ }; // Neuer, verwendet Sekunden und Nanosekunden, um Zeit zu sparen. do_gettimeofday() Diese Funktion füllt eine Zeigervariable, die auf die Struktur timeval zeigt, mit den üblichen Sekunden oder Mikrosekunden. Der Prototyp sieht wie folgt aus: #include <linux/time.h> void do_gettimeofday(Struktur timeval *tv); current_kernel_time() Mit dieser Funktion können Sie die Zeitspezifikation abrufen #include <linux/time.h> Struktur Zeitspezifikation current_kernel_time(void); /******************** *Verzögerte Ausführung der festgelegten Zeit********************/ Gerätetreiber müssen die Ausführung bestimmten Codes häufig für einen bestimmten Zeitraum verzögern, normalerweise, um der Hardware die Fertigstellung einer bestimmten Aufgabe zu ermöglichen. Verzögerungen, die länger als die Timerperiode (auch als Taktimpuls bezeichnet) sind, können durch die Verwendung der Systemuhr erreicht werden, während sehr kurze Verzögerungen durch Softwareschleifen erreicht werden können. (1) Kurze Verzögerung Bei Verzögerungen von bis zu einigen zehn Millisekunden gibt es keine Möglichkeit, den System-Timer zu verwenden. Das System bietet die folgenden Verzögerungsfunktionen über Softwareschleifen: #include <linux/delay.h> /* Tatsächlich in <asm/delay.h> */ void ndelay(unsigned long nsecs); /*Verzögerung Nanosekunden*/ void udelay(unsigned long usecs); /*Verzögerung in Mikrosekunden*/ void mdelay(unsigned long msecs); /*Verzögerung in Millisekunden*/ Bei diesen drei Verzögerungsfunktionen handelt es sich um Wartefunktionen, und während des Verzögerungsvorgangs können keine anderen Aufgaben ausgeführt werden. Tatsächlich ist eine Nanosekundenpräzision derzeit nicht auf allen Plattformen möglich. (2) Lange Verzögerung a. Geben Sie den Prozessor auf, bevor die Verzögerung abläuft während(Zeit_vor(jiffies, j1)) Zeitplan(); Während der Wartezeit kann der Prozessor freigegeben werden, das System kann jedoch nicht in den Leerlaufmodus wechseln (da dieser Vorgang immer geplant wird), was der Energieeinsparung nicht förderlich ist. b. Timeout-Funktion #include <linux/sched.h> signiertes langes Schedule_Timeout (signiertes langes Timeout); Anwendung: setze_aktuellen_Zustand(TASK_UNTERBRECHUNG); schedule_timeout(2*HZ); /* 2 Sekunden schlafen*/ Der Prozess wird nach 2 Sekunden aufgeweckt. Wenn Sie nicht durch den Benutzerbereich unterbrochen werden möchten, können Sie den Prozessstatus auf TASK_UNINTERRUPTIBLE setzen. msleep ssleep // Sekunden (3) Warteschlange Lange Verzögerungen können auch durch Warteschlangen erreicht werden. Während der Verzögerung schläft der aktuelle Prozess in der Warteschlange. Wenn ein Prozess schläft, muss er basierend auf dem Ereignis, auf das er wartet, mit einer Warteschlange verknüpft werden. a. Warteschlange bekannt geben Die Warteschlange ist eigentlich eine prozessverknüpfte Liste, die alle Prozesse enthält, die auf ein bestimmtes Ereignis warten. #include <linux/wait.h> Struktur __wait_queue_head { Spinlock_t-Sperre; Struktur list_head task_list; }; Typdefinitionsstruktur __wait_queue_head wait_queue_head_t; Um einen Prozess zur Warteschlange hinzuzufügen, muss der Treiber zunächst einen Warteschlangenkopf im Modul deklarieren und initialisieren. Statische Initialisierung DECLARE_WAIT_QUEUE_HEAD(Name); Dynamische Initialisierung wait_queue_head_t meine_Warteschlange; init_waitqueue_head(&meine_Warteschlange); b. Wartefunktion Ein Prozess kann für eine festgelegte Zeit in einer Warteschlange schlafen, indem er die folgende Funktion aufruft: #include <linux/wait.h> langes Wait_event_Timeout (Wait_Queue_Head_t q, Bedingung, langes Timeout); langes wait_event_interruptible_timeout(wait_queue_head_t q, Bedingung, langes Timeout); Nach dem Aufruf dieser beiden Funktionen schläft der Prozess in der angegebenen Warteschlange q, kehrt jedoch zurück, wenn das Zeitlimit abgelaufen ist. Wenn das Zeitlimit abgelaufen ist, wird 0 zurückgegeben. Wenn der Prozess durch ein anderes Ereignis aufgeweckt wird, wird die verbleibende Zeit zurückgegeben. Wenn keine Wartebedingung vorliegt, setze Bedingung auf 0 Anwendung: wait_queue_head_t warte; init_waitqueue_head(&warten); wait_event_interruptible_timeout(warten, 0, 2*HZ); /*Der aktuelle Prozess schläft 2 Sekunden in der Warteschlange */ (4) Kernel-Timer Eine andere Möglichkeit, die Ausführung einer Aufgabe zu verzögern, ist die Verwendung eines Kernel-Timers. Im Gegensatz zu den vorherigen Verzögerungsmethoden blockiert der Kernel-Timer den aktuellen Prozess nicht. Das Starten eines Kernel-Timers erklärt lediglich, dass eine Aufgabe irgendwann in der Zukunft ausgeführt wird, und der aktuelle Prozess wird weiterhin ausgeführt. Verwenden Sie keine Timer für anspruchsvolle Echtzeitaufgaben Der Timer wird durch die Struktur timer_list dargestellt, die in <linux/timer.h> definiert ist. Struktur timer_list{ Struktur list_head entry; /* Timer-verknüpfte Liste */ unsigned long expires; /* Zeitwert in Augenblicken*/ Spinlock_t-Sperre; void(*function)(unsigned long); /* Timer-Verarbeitungsfunktion*/ unsigned long data; /* An die Timer-Verarbeitungsfunktion übergebene Parameter*/ } Der Kernel stellt eine Reihe von Schnittstellen zur Verwaltung von Timern in <linux/timer.h> bereit. a. Erstellen Sie einen Timer b. Initialisieren Sie den Timer init_timer(&mein_timer); /* Datenstruktur füllen */ my_timer.expires = Augenblicke + Verzögerung; mein_timer.data = 0; my_timer.function = my_function; /*Funktion, die aufgerufen wird, wenn der Timer abläuft*/ c. Timer-Ausführungsfunktion Der Prototyp der Timeout-Verarbeitungsfunktion sieht wie folgt aus: void meine_timer_funktion(vorzeichenlose lange Daten); Mit dem Datenparameter können Sie mehrere Timer mit einer Verarbeitungsfunktion verwalten. Sie können die Daten auf 0 setzen d. Aktivieren Sie den Timer Sobald der Timer aktiviert wird, beginnt er zu laufen. e. Ändern der Timeout-Periode eines aktivierten Timers mod_timer(&mein_timer, jiffies+ney_delay); Es kann für Timer verwendet werden, die initialisiert, aber noch nicht aktiviert wurden. Wenn der Timer beim Aufruf nicht aktiviert ist, gibt es 0 zurück, andernfalls 1. Sobald mod_timer zurückkehrt, wird der Timer aktiviert. f. Timer löschen Es können sowohl aktivierte als auch inaktivierte Timer verwendet werden. Ist der Timer beim Aufruf inaktiv, gibt er 0 zurück, andernfalls 1. Kein Anruf bei abgelaufenen Timern nötig, sie werden automatisch gelöscht g. Synchrones Löschen Stellen Sie in SMP-Systemen sicher, dass alle Timer-Handler bei der Rückkehr beendet werden. Kann nicht im Interrupt-Kontext verwendet werden. /******************** *Verzögerte Ausführung mit ungewisser Zeit********************/ (1) Was ist eine unbestimmte Verzögerung? Im vorherigen Abschnitt wurde die verzögerte Ausführung für eine bestimmte Zeit vorgestellt. Diese Situation tritt jedoch häufig beim Schreiben von Treibern auf: Das Benutzerbereichsprogramm ruft die Lesefunktion auf, um Daten vom Gerät zu lesen, aber derzeit werden im Gerät keine Daten generiert. An diesem Punkt besteht die Standardoperation der Lesefunktion des Treibers darin, in den Ruhemodus zu wechseln und zu warten, bis Daten im Gerät vorhanden sind. Bei dieser Art des Wartens handelt es sich um eine Verzögerung auf unbestimmte Zeit, die normalerweise durch die Verwendung eines Schlafmechanismus erreicht wird. (2) Schlaf Das Schlafen basiert auf Warteschlangen. Wir haben die wait_event-Funktionsreihe bereits eingeführt, aber jetzt haben wir keine feste Schlafzeit mehr. Wenn ein Prozess in den Ruhezustand versetzt wird, wird er mit einem speziellen Status gekennzeichnet und aus der Ausführungswarteschlange des Schedulers entfernt. Bis bestimmte Ereignisse eintreten, z. B. das Gerät Daten empfängt, wird der Prozess in den Ausführungszustand zurückgesetzt und gelangt zur Planung in die Ausführungswarteschlange. Die Header-Datei der Sleep-Funktion ist <linux/wait.h> und die spezifische Implementierungsfunktion befindet sich in kernel/wait.c. a. Ruheregeln
b. Initialisierung der Warteschlange Siehe vorherigen Artikel c. Schlaffunktion Der einfachste Ruhemodus in Linux ist das Makro wait_event. Dieses Makro prüft die Bedingung, auf die der Prozess während der Implementierung des Ruhezustands wartet. 1. void warte_ereignis( warte_warteschlangenkopf_t q, int-Bedingung); 2. int wait_event_interruptible( warte_warteschlangenkopf_t q, int-Bedingung);
Weckfunktion Wenn unser Prozess in den Ruhezustand wechselt, muss er von einem anderen Ausführungsthread (das kann ein anderer Prozess oder eine Routine zur Interrupt-Behandlung sein) geweckt werden. Weckfunktion: #include <linux/wait.h> 1. void wake_up( wait_queue_head_t *Warteschlange); 2. void wake_up_interruptible( wait_queue_head_t *Warteschlange); wake_up weckt alle Prozesse, die in der angegebenen Warteschlange warten. Und wake_up_interruptible weckt Prozesse, die sich im unterbrechbaren Ruhezustand befinden. In der Praxis besteht die Konvention darin, wake_up zu verwenden, wenn wait_event verwendet wird, und wake_up_interruptible zu verwenden, wenn wait_event_interruptible verwendet wird. Zusammenfassen Das Obige ist der vollständige Inhalt dieses Artikels. Ich hoffe, dass der Inhalt dieses Artikels einen gewissen Lernwert für Ihr Studium oder Ihre Arbeit hat. Vielen Dank für Ihre Unterstützung von 123WORDPRESS.COM. Wenn Sie mehr darüber erfahren möchten, schauen Sie sich bitte die folgenden Links an Das könnte Sie auch interessieren:
|
<<: MySQL-Installationstutorial unter Centos7
In einer Tabelle können Sie die Farbe des oberen ...
Vorwort Im aktuellen JavaScript gibt es kein Konz...
Dieser Artikel beschreibt anhand eines Beispiels,...
Inhaltsverzeichnis 1. Vorbereitung 2. Befehlszeil...
Das in diesem Artikel beschriebene Layout gleiche...
ElementUI implementiert das Tutorial zum Paginier...
Ich erstelle schon lange Websites, habe aber immer...
MySQL-Einstellungscode für grüne Version und Fehl...
Inhaltsverzeichnis 1. Speichermodell und Laufzeit...
1. v-on-Ereignisüberwachung Um DOM-Ereignisse abz...
1. Setzen Sie den HTML-Code der Maskenebene und d...
Was ist Element-UI element-ui ist eine auf Vue.js...
Beispielverwendung Code kopieren Der Code lautet w...
Inhaltsverzeichnis 1. Listendurchlauf 2. Die Roll...
Was ist CSS? CSS (Abkürzung für Cascading Style S...