Einführung in Linux --- Speichern und Erfassen von Signalen

Einige Konzepte von Signalen

1. Der Prozess empfängt verschiedene Signale, und die tatsächliche Verarbeitung des Signals durch das Programm wird als Übermittlung des Signals bezeichnet.
2. Wir haben zuvor gesagt, dass ein Prozess, wenn er ein Signal empfängt, das Signal möglicherweise nicht sofort verarbeitet, sondern das Signal zunächst speichert. Dann nennen wir den Zustand zwischen der Erzeugung und dem Eintreffen des Signals „Signal ausstehend“.
3. Ein Prozess kann ein Signal blockieren, sodass der Prozess selbst dann keine Verarbeitung durchführt, wenn das Signal an den entsprechenden Prozess gesendet wird.
4. Das blockierte Signal bleibt im Wartezustand und die Aktion des Signals wird nur ausgeführt, wenn der Prozess die Blockierung des Signals aufhebt.
5. Blockieren und Ignorieren sind zwei verschiedene Dinge. Wenn ein Signal blockiert wird, bedeutet dies, dass die Aktion des Signals nicht ausgeführt wird, selbst wenn das Signal empfangen wird. Ignorieren ist eine Aktion, die basierend auf dem Signal nach dem Empfang des Signals ausgeführt wird. Es ist nur so, dass diese Aktion ignoriert wird, was bedeutet, dass nichts unternommen wird.

Signalerhaltung

In früheren Studien wussten wir, dass das Betriebssystem das empfangene Signal für jeden Prozess speichern sollte und der Speicherort eine Bitmap in task_struct ist. Dies war die Speichermethode, über die wir damals gesprochen haben, aber es gibt drei Signalzustände, z B. Ob das Signal empfangen wird, ob das Signal blockiert ist und welche Aktion dem Signal entspricht und ob die Aktion angepasst und geändert wurde. Daher reicht es für den Kernel definitiv nicht aus, ein Bild zum Speichern des Signals zu verwenden, also drei werden in tatsächlichen Anwendungen verwendet. Die Signale werden zusammen in zwei Tabellen gespeichert. Diese drei Tabellen sind: Pending-Tabelle, Blocktabelle und Handler-Tabelle. Als Nächstes werden wir die Funktionen dieser drei Tabellen einzeln vorstellen.

ausstehende Tabelle

Die Pinding-Tabelle ist im Wesentlichen eine Bitmap. Wir sagten zuvor, dass der Prozess jedes Mal, wenn er ausgeführt wird, ein Signal empfängt. Dieses Signal wird nicht sofort verarbeitet, daher muss der Prozess das Signal speichern. Die Möglichkeit, das Signal zu speichern, ist also eine Bitmap Diese Bitmap ist die ausstehende Tabelle. Die Bitposition der Bitmap gibt an, welches Signal vorliegt. Der Wert des Bits gibt an, ob das Signal empfangen wurde. Wenn das Signal empfangen wird, wird der Wert der entsprechenden Position auf 1 geändert. Wenn die Wird kein Signal empfangen, ist der Wert der entsprechenden Position 0. .

Fügen Sie hier eine Bildbeschreibung ein

Blocktabelle

Die Blocktabelle ist ebenfalls eine Bitmap. Die Position der Bitmap stellt die Signalnummer dar. Der Wert der Bitmap stellt dar, ob das entsprechende Signal blockiert ist. Das blockierte Signal wird nicht übermittelt. Das Signal wird erst übermittelt, wenn die Blockierung aufgehoben wird Wir können einige Signale blockieren, auch wenn die entsprechenden Signale nicht empfangen werden. Die Logik hier kann durch den folgenden Pseudocode ausgedrückt werden:

if(1<<(signo)&pcb->block))
{
    
    
	//信号是被阻塞的,不递达
}
else
{
    
    
	if((1<<(signo-1))&pcb->pending)
	{
    
    
		//递达该信号
	}
}

Dies kann hier verstanden werden. Das Betriebssystem scannt die Bitmap auf der Leiterplatte, um festzustellen, ob ein Signal vorhanden ist, das verarbeitet werden muss. Wenn der Wert des dem Block entsprechenden Signals 1 ist, wird er direkt übersprungen. Wenn die Der dem Block entsprechende Wert ist 0, er wird übersprungen. Überprüfen Sie einfach weiterhin den Wert, der der Panding-Bitmap entspricht. Wenn der Wert 1 ist, führen Sie die entsprechende Methode aus. Dann ist dies die Funktion der Blocktabelle.
Fügen Sie hier eine Bildbeschreibung ein

Handler-Tabelle

Die Handlertabelle ist ein Array von Funktionszeigern. Jedes Element ist ein Funktionszeiger. Die Position (Index) des Arrays stellt die Signalnummer dar. Der dem Array-Index entsprechende Inhalt stellt die Verarbeitungsmethode des entsprechenden Signals dar. Die Signalfunktion kann Ändern Sie das entsprechende Signal. Methode, dann besteht dies im Wesentlichen darin, den Zeiger des entsprechenden Elements in der Handler-Tabelle zu ändern. Wenn das Signal geliefert wird, kann die Methode zur Verarbeitung des Signals in der Handler-Tabelle basierend auf dem Wert des Signals gefunden werden und hingerichtet. Dann sieht das Bild hier wie folgt aus:
Fügen Sie hier eine Bildbeschreibung ein

Daraus können wir drei Schlussfolgerungen ziehen: Wenn ein Signal nicht generiert wird, verhindert das nicht, dass es zuerst blockiert wird, da wir nur den Wert in der Blocktabelle ändern müssen und es nichts mit der Pending-Tabelle zu tun hat, die aufzeichnet ob das Signal empfangen wird. Wenn während des Blockiervorgangs ein Signal empfangen wird, ändern wir den entsprechenden Wert in der ausstehenden Bitmap und gehen nicht zur Methode in der Handlertabelle. 2. Warum der Prozess das Signal erkennen kann, liegt daran, dass dort sind drei Tabellen im Kernel. Tabellen ermöglichen es Prozessen, Signale zu erkennen und entsprechende Signale zu verarbeiten. 3. Wenn das SIGQUIT-Signal nicht generiert wurde, wird es nach der Generierung des SIGQUIT-Signals blockiert und seine Verarbeitungsaktion ist der benutzerdefinierte Funktions-Singhandler. Was passiert, wenn ein Signal mehrmals generiert wird, bevor der Prozess es entsperrt? POSIX.1 ermöglicht es dem System, das Signal einmal oder mehrmals zu liefern. Linux implementiert dies: Reguläre Signale, die vor der Zustellung mehrmals generiert werden, werden nur einmal gezählt, während Echtzeitsignale, die vor der Zustellung mehrmals generiert werden, nacheinander in eine Warteschlange gestellt werden können.

Signalerfassung

Das Signal wird nicht sofort verarbeitet, wenn es erzeugt wird, sondern zum richtigen Zeitpunkt. Wann ist also der richtige Zeitpunkt? Und wir haben oben erwähnt, dass das Betriebssystem zu bestimmten Zeiten die Blocktabelle und die Pending-Tabelle überprüft. Wann bezieht sich diese Zeit also? Um diese Probleme zu lösen, müssen wir darüber sprechen, was der Kernel und was der Benutzermodus ist.

Kernelmodus und Benutzermodus

Der Code, den wir selbst schreiben, wird nach dem Kompilieren und Ausführen im Benutzermodus ausgeführt. Wenn wir jedoch den Code schreiben, greifen wir zwangsläufig auf die Ressourcen des Betriebssystems (getpid, waitpid) und die Hardwareressourcen (printf, read, write) zu Um auf die Ressourcen des Betriebssystems oder auf Hardwareressourcen zuzugreifen, müssen wir direkt oder indirekt auf die vom System bereitgestellten Ausreden zugreifen und über diese Schnittstellen auf die Ressourcen des Betriebssystems zugreifen. Wir nennen diese Schnittstellen Systemaufrufe. Normale Benutzer können das System nicht
Fügen Sie hier eine Bildbeschreibung ein
aufrufen Ruft mit ihrer eigenen Identität an und muss ihre Identität in den Kernel-Modus ändern.
Das ist so, als ob wir, wenn wir die Basis eines Unternehmens sind, das Büro des Vizepräsidenten kaum direkt betreten oder verlassen können, aber wenn sich unsere Identität ändert, wenn wir der CEO von werden Im Unternehmen können wir das Büro des Vizepräsidenten nach Belieben betreten und verlassen. Wenn unsere Identität zum Kernel-Status wird, sind wir berechtigt, den vom Kernel-Status bereitgestellten Code zu verwenden oder darauf zuzugreifen. Die Person, die den Systemaufruf tatsächlich ausführt, ist Prozess, Die Identität ist jedoch der Kernelstatus. Da die Identitätskonvertierung erfolgt, wenn der Benutzerstatus den Kernelstatus aufruft, sind Systemaufrufe häufig zeitaufwändig. Vermeiden Sie daher häufige Systemaufrufe. Hier liegt jedoch ein Problem vor. Sie sagten, dass der Prozess so ist
Fügen Sie hier eine Bildbeschreibung ein
Nur dieser Prozess hat zwei Identitäten. Wie erreicht das Betriebssystem dies? Hier müssen wir also das Register erwähnen.

Es gibt eine große Anzahl von Registern in der CPU, und die Register sind in zwei Typen unterteilt: 1. Sichtbare Register, 2. Unsichtbare Register. Obwohl Register in zwei Typen unterteilt sind, solange die Register eng mit dem aktuellen Prozess verknüpft sind Wir nennen sie beispielsweise Kontextdaten. In der CPU gibt es ein Register, das speziell dazu dient, auf die Platine des aktuellen Prozesses zu verweisen, sodass Sie schnell erkennen können, welcher Prozess gerade ausgeführt wird. Ein weiteres Beispiel ist, dass es ein Register gibt Hier wird die Startadresse der aktuellen Seitentabelle gespeichert, sodass Sie sie schnell finden können. Die Seitentabelle des aktuellen Prozesses.

Es gibt auch ein Register mit dem Namen CR3 in der CPU. Der Inhalt dieses Registers stellt die Ausführungsebene des aktuellen Prozesses dar. 0 stellt den Kernel-Status und 3 den Benutzerstatus dar. Wenn Sie daher feststellen möchten, ob der aktuelle Prozess der ist Benutzerstatus oder Kernelstatus, Sie müssen ihn sich nur ansehen. Der Inhalt in CR3 reicht aus. Hier gibt es ein Problem: Ich bin ein Prozess. Wie gelangt der Prozess zum Kernel, um die entsprechende Methode auszuführen? Der zuvor erwähnte Prozessadressraum bezieht sich häufig auf den Benutzerraum. Der Code, den wir schreiben oder für den Raum anwenden, wird durch den Adressraum auf Benutzerebene + die Seitentabelle abgebildet. Die Größe dieses Raums beträgt 3 GB.
Fügen Sie hier eine Bildbeschreibung ein

Die Seitentabelle hier ist eine Seitentabelle auf Benutzerebene. Jeder Prozess verfügt über eine Kopie der Seitentabelle, aber die Gesamtgröße des Adressraums beträgt 4 GB. Der Speicherplatz auf Benutzerebene belegt nur 3 GB. Wofür werden also die verbleibenden 1 GB verwendet? ? Wir nennen diesen 1-GB-Speicherplatz Speicherplatz auf Kernel-Ebene. Dieser Speicherplatz entspricht auch einer Seitentabelle. Diese Seitentabelle wird als Seitentabelle auf Kernel-Ebene bezeichnet. Die Funktion dieser Seitentabelle besteht darin, Code auf Betriebssystemebene aus der virtuellen Kernel-Adresse abzubilden Raum. zur Erinnerung,
Fügen Sie hier eine Bildbeschreibung ein

Da nur eine Kopie des Betriebssystemcodes vorhanden ist, wird dieser Code beim Starten der Maschine in den Speicher geladen, und jeder Prozess muss den Betriebssystemcode verwenden, sodass nur eine Kopie der Seitentabelle auf Kernelebene vorhanden ist Jeder Prozess übergibt dies Die Seitentabelle ordnet den im Speicher gefundenen Betriebssystemcode zu. Jeder Prozess verfügt über einen eigenen Adressraum (der ausschließlich vom Benutzerraum verwendet wird) und einen Kernelraum (der 3 bis 4 GB des Adressraums jedes Prozesses zugeordnet ist), sodass der Prozess müssen Um auf die Schnittstelle des Betriebssystems zuzugreifen, müssen Sie nur in Ihren eigenen Adressraum springen. Springen Sie vom Benutzerbereich in den Kernelbereich und dann von der Seitentabelle auf Benutzerebene, um auf den Kernelcode im Speicher zuzugreifen. Jeder Prozess Der Prozessadressraum aller Prozesse verfügt über 3 bis 4 GB Inhalt und alle teilen sich eine Seitentabelle auf Kernelebene. Unabhängig davon, wie der Prozess wechselt, werden die 3 bis 4 GB Inhalt im Prozessadressraum nicht geändert. Warum können Benutzer Kernel-Schnittstellen oder -Daten ausführen und darauf zugreifen? Der Grund dafür ist, dass das Betriebssystem, wenn es feststellt, dass Sie auf die Kerneldaten und den Kernelcode zugreifen möchten, den Prozess dabei unterstützt, den Inhalt des CR3-Registers zu ändern, sodass es vom Benutzermodus in den Kernelmodus wechselt. Dann haben Sie das Recht dazu Auf die Kerneldaten zugreifen. Warum kann das Betriebssystem dann nicht wissen, auf welche Daten und Codes wir auf den Kernel zugreifen möchten? Die Antwort lautet: Wenn wir auf die Schnittstelle des Betriebssystems zugreifen möchten, wird diese zunächst im Benutzermodus ausgeführt. Wenn die Systemaufrufschnittstelle im Benutzermodus ausgeführt wird, besteht das Wichtigste, was die Systemaufrufschnittstelle tut, darin, sie zu ändern Zustand Nr. 3 im Register auf Zustand Nr. 0. Das heißt, der Benutzermodus wird in den Kernelmodus geändert und dann wird auf den Sprungbereich zu den Kerneldaten und -code zugegriffen. Dies ist dann das Konzept des Benutzers Modus und Kernelmodus.

Signalerfassung

Wir sagen, dass das Betriebssystem das Signal zum richtigen Zeitpunkt verarbeiten wird. Der richtige Zeitpunkt ist also, das Signal zu verarbeiten, wenn es vom Kernel-Modus in den Benutzermodus zurückkehrt. Dies bedeutet, dass wir zuvor in den Kernel-Modus gelangt sein müssen.

Fügen Sie hier eine Bildbeschreibung ein

Beim Ausführen von Systemaufrufen und Prozesswechseln (die Ausführung eines Prozesses ist noch nicht abgeschlossen) muss er in die laufende Warteschlange oder Warteschlange gestellt werden. Wenn er in die Warteschlange gestellt wird, muss ich als Betriebssystem eingegeben werden, also muss ich zuerst werden der Kernel. Der Zustand wird zum Kernel-Zustand)
Fügen Sie hier eine Bildbeschreibung ein

Wenn das Betriebssystem in den Kernel-Status wechselt und kurz davor steht, in den Normalzustand zurückzukehren, überprüft es unter anderem die drei Tabellen im Kernel.

Fügen Sie hier eine Bildbeschreibung ein

Wenn das Signal im Block 0 ist und das anstehende Signal ebenfalls 0 ist, wird das nächste Signal überprüft. Wenn der Wert im Block 1 ist, wird das Signal nicht ausgeführt, selbst wenn der Wert anstehend 1 ist. Wenn das Der Wert des Blocks ist 0 und der Penging-Wert ist 1, das Signal wird ausgeführt und dann wird die entsprechende Methode im Handler gefunden. Zu den Verarbeitungsmethoden gehören Standard, Ignorieren und Benutzerdefiniert. 70 % der Standardmethoden beenden den Prozess weil die aktuelle Identität Es befindet sich im Kernelmodus, sodass Sie den aktuellen Prozess problemlos beenden können. Ignorieren bedeutet, das Signal zu verarbeiten, aber die Verarbeitungsaktion besteht darin, nichts zu tun. Setzen Sie daher einfach das entsprechende Signal auf 0. Die Implementierung ist benutzerdefinierte Wenn sich die Methode im Benutzerstatus befindet, stellt sich hier die Frage: Können wir Benutzercode als Kernelstatus ausführen? Aus technischer Sicht können wir im Kernel-Modus auf den Code des Benutzers zugreifen, aus gestalterischer Sicht jedoch nicht, da das Betriebssystem niemandem vertraut und möglicherweise Vorgänge in der Handler-Methode gefährlich sind Das System und das Betriebssystem können nicht erkennen, ob diese Vorgänge sicher oder schädlich sind. Wenn wir also den Code des Benutzers im Kernelmodus ausführen, kann dieser Teil des Codes von böswilligen Elementen verwendet werden und dann einige nicht autorisierte illegale Aktionen ausführen Auch technisch gesehen ist dies aus Sicht des Betriebssystems möglich, aber das Betriebssystem lässt dies nicht zu. Wenn Sie benutzerdefinierten Code ausführen möchten, muss das Betriebssystem daher einen bestimmten Aufruf verwenden, um seine Identität aus dem zu konvertieren Wechseln Sie vom Kernelmodus in den Benutzermodus und führen Sie dann den benutzerdefinierten Code aus. Wenn zu diesem Zeitpunkt ein Problem mit der benutzerdefinierten Methode auftritt, ist dies das Problem des Benutzers selbst. Wenn er zu diesem Zeitpunkt etwas Schlimmes tun möchte, wird er dies tun vom Betriebssystem gesteuert werden.
Fügen Sie hier eine Bildbeschreibung ein

Nach der Ausführung der benutzerdefinierten Methode können wir nicht direkt zu der Stelle zurückkehren, an der die Methode zuvor aufgerufen wurde, da wir nicht wissen können, wo der Sprung ursprünglich gemacht wurde. Wir können im Benutzermodus nicht von einer Stelle zur anderen springen. Hier muss ein Betriebssystem vorhanden sein Unterstützt, da das Betriebssystem Ihre Kontextinformationen speichert, wenn es vom Benutzermodus in den Kernelmodus wechselt. Nach der Ausführung der benutzerdefinierten Methode müssen Sie also vom Benutzermodus zum Kernel zurückspringen und dann über ein bestimmtes System vom Kernelmodus zurückkehren Rufen Sie an. Gehen Sie im Benutzermodus zur letzten Stelle und führen Sie die Ausführung rückwärts fort.
Fügen Sie hier eine Bildbeschreibung ein
Dann ist dies der gesamte Prozess der Signalerfassung. Ich hoffe, jeder kann es verstehen.

Supongo que te gusta

Origin blog.csdn.net/qq_68695298/article/details/133466299
Recomendado
Clasificación