Im Allgemeinen zu töten Prozesse erzeugen wir Signale wie SIGKILL
, SIGTSTP
usw.
Aber wie ist bekannt, wer dieses bestimmte Signal bestellt hat, wer es an einen bestimmten Prozess gesendet hat und wie Signale im Allgemeinen ihre Operationen ausführen? Wie funktionieren Signale intern?
kill
signals
architecture
Varun Chhangani
quelle
quelle
Antworten:
Die 50.000 Fuß Ansicht ist, dass:
Ein Signal wird entweder vom Kernel intern generiert (zum Beispiel,
SIGSEGV
wenn auf eine ungültige Adresse zugegriffen wird oderSIGQUIT
wenn Sie Ctrl+ drücken \) oder von einem Programm, das denkill
Syscall verwendet (oder von mehreren verwandten).Wenn es sich um einen der Systemaufrufe handelt, bestätigt der Kernel, dass der aufrufende Prozess über ausreichende Berechtigungen zum Senden des Signals verfügt. Wenn nicht, wird ein Fehler zurückgegeben (und das Signal tritt nicht auf).
Wenn es sich um eines von zwei speziellen Signalen handelt, reagiert der Kernel bedingungslos darauf, ohne dass Eingaben vom Zielprozess erforderlich sind. Die beiden Spezialsignale sind SIGKILL und SIGSTOP. Alle folgenden Informationen zu Standardaktionen, Blockierungssignalen usw. sind für diese beiden irrelevant.
Als nächstes findet der Kernel heraus, was mit dem Signal zu tun hat:
Für jeden Prozess ist jedem Signal eine Aktion zugeordnet. Es gibt eine Reihe von Standardwerten ist, und Programme festlegen können unterschiedliche Verwendung
sigaction
,signal
usw. Dazu gehören Dinge wie „ignorieren sie vollständig“, „den Prozess töten“, „den Prozess mit einem Core - Dump töten“, „den Prozess stoppen“, etc.Programme können auch die Übermittlung von Signalen ("gesperrt") signalweise ausschalten. Dann bleibt das Signal anstehend, bis es entsperrt wird.
Programme können anfordern, dass der Kernel nicht selbst eine Aktion ausführt, sondern das Signal entweder synchron (mit
sigwait
usw. odersignalfd
) oder asynchron (durch Unterbrechen des Prozesses und Aufrufen einer bestimmten Funktion) an den Prozess liefert .Es gibt eine zweite Gruppe von Signalen, die als "Echtzeitsignale" bezeichnet werden und keine spezielle Bedeutung haben. Außerdem können mehrere Signale in die Warteschlange gestellt werden (normale Signale stellen nur jeweils eines in die Warteschlange, wenn das Signal blockiert ist). Diese werden in Multithread-Programmen verwendet, damit die Threads miteinander kommunizieren können. Einige werden zum Beispiel in der POSIX-Thread-Implementierung von glibc verwendet. Sie können auch zur Kommunikation zwischen verschiedenen Prozessen verwendet werden (Sie können beispielsweise mehrere Echtzeitsignale verwenden, um ein fooctl-Programm eine Nachricht an den foo-Daemon senden zu lassen).
Versuchen Sie für eine Nicht-50.000-Fuß-Ansicht
man 7 signal
und auch die Kernel-interne Dokumentation (oder Quelle).quelle
Die Signalimplementierung ist sehr komplex und kernelspezifisch. Mit anderen Worten, verschiedene Kernel implementieren Signale unterschiedlich. Eine vereinfachte Erklärung lautet wie folgt:
Die CPU, basierend auf einem speziellen Registerwert, hat eine Adresse im Speicher, wo sie erwartet, eine "Interrupt-Deskriptor-Tabelle" zu finden, die tatsächlich eine Vektortabelle ist. Für jede mögliche Ausnahme gibt es einen Vektor, z. B. Division durch Null oder Trap wie INT 3 (Debug). Wenn die CPU auf die Ausnahme stößt, speichert sie die Flags und den aktuellen Befehlszeiger auf dem Stapel und springt dann zu der durch den relevanten Vektor angegebenen Adresse. In Linux zeigt dieser Vektor immer in den Kernel, in dem es einen Exception-Handler gibt. Die CPU ist nun fertig und der Linux-Kernel übernimmt.
Beachten Sie, dass Sie auch eine Ausnahme von der Software auslösen können. Zum Beispiel drückt der Benutzer CTRL- C, dann geht dieser Aufruf an den Kernel , die ihren eigenen Exception - Handler aufruft. Im Allgemeinen gibt es verschiedene Möglichkeiten, auf den Handler zuzugreifen, aber unabhängig davon, dass das Gleiche passiert: Der Kontext wird auf dem Stapel gespeichert, und der Ausnahmehandler des Kernels wird angesprungen.
Der Ausnahmehandler entscheidet dann, welcher Thread das Signal empfangen soll. Wenn so etwas wie eine Division durch Null auftritt, ist es einfach: Der Thread, der die Ausnahme verursacht hat, erhält das Signal. Bei anderen Signaltypen kann die Entscheidung jedoch sehr komplex sein, und in einigen ungewöhnlichen Fällen kann ein mehr oder weniger zufälliger Thread auftreten bekomme das Signal.
Um das Signal zu senden, was der Kernel tut, muss zuerst ein Wert eingestellt werden, der den Signaltyp
SIGHUP
oder was auch immer angibt . Dies ist nur eine ganze Zahl. Jeder Prozess hat einen Speicherbereich "Pending Signal", in dem dieser Wert gespeichert wird. Dann erstellt der Kernel eine Datenstruktur mit den Signalinformationen. Diese Struktur enthält ein Signal "Disposition", das standardmäßig, ignoriert oder verarbeitet werden kann. Der Kernel ruft dann seine eigene Funktion aufdo_signal()
. Die nächste Phase beginnt.do_signal()
entscheidet zunächst, ob es das Signal verarbeiten soll. Wenn es sich zum Beispiel um eine Tötung handelt , wirddo_signal()
der Prozess am Ende der Geschichte abgebrochen. Ansonsten sieht es nach der Disposition aus. Wenn die Disposition Standard ist, wirddo_signal()
das Signal gemäß einer vom Signal abhängigen Standardrichtlinie verarbeitet. Wenn die Disposition Handle ist, bedeutet dies, dass es im Anwenderprogramm eine Funktion gibt, die für die Behandlung des betreffenden Signals ausgelegt ist, und der Zeiger auf diese Funktion befindet sich in der oben genannten Datenstruktur. In diesem Fall ruft do_signal () eine andere Kernelfunktion auf,handle_signal()
, der dann den Vorgang durchläuft, in den Benutzermodus zurückzukehren und diese Funktion aufzurufen. Die Details dieser Übergabe sind äußerst komplex. Dieser Code in Ihrem Programm wird normalerweise automatisch mit Ihrem Programm verknüpft, wenn Sie die Funktionen in verwendensignal.h
.Durch geeignete Prüfung des anstehenden Signalwerts kann der Kernel feststellen, ob der Prozess alle Signale verarbeitet. Ist dies nicht der Fall, werden geeignete Maßnahmen ergriffen, die den Prozess abhängig vom Signal möglicherweise in den Ruhezustand versetzen oder beenden oder andere Maßnahmen ergreifen.
quelle
Obwohl diese Frage beantwortet wurde, möchte ich einen detaillierten Ablauf von Ereignissen im Linux-Kernel veröffentlichen.
Dies wurde vollständig von Linux-Posts kopiert : Linux-Signale - Interna im Blog „Linux posts“ unter sklinuxblog.blogspot.in.
Signal User Space C-Programm
Beginnen wir mit dem Schreiben eines einfachen C-Programms für den Signalbenutzerraum:
Dieser Code weist einen neuen Handler für das SIGINT-Signal zu. SIGINT kann mit der Tastenkombination Ctrl+ an den laufenden Prozess gesendet werden C. Wenn Ctrl+ Cgedrückt wird, wird das asynchrone Signal SIGINT an die Task gesendet. Dies entspricht auch dem Senden des
kill -INT <pid>
Befehls in einem anderen Terminal.Wenn Sie eine
kill -l
(das ist ein KleinbuchstabeL
, der für "Liste" steht) ausführen, werden Sie die verschiedenen Signale kennen, die an einen laufenden Prozess gesendet werden können.Folgende Tastenkombination kann auch zum Senden bestimmter Signale verwendet werden:
Wenn Sie das obige C-Programm kompilieren und ausführen, erhalten Sie die folgende Ausgabe:
Auch mit Ctrl+ Coder wird
kill -2 <pid>
der Vorgang nicht beendet. Stattdessen führt es den Signalhandler aus und kehrt zurück.Wie das Signal an den Prozess gesendet wird
Wenn wir die Interna des Signals sehen, das an einen Prozess gesendet wird, und Jprobe mit dump_stack auf
__send_signal
function setzen, sehen wir folgenden Aufruf-Trace:Die Hauptfunktion zum Senden des Signals lautet also:
Jetzt ist alles eingerichtet und die notwendigen Änderungen werden
task_struct
am Prozess vorgenommen.Umgang mit Signal
Das Signal wird von einem Prozess geprüft / verarbeitet, wenn es vom Systemaufruf zurückkommt oder wenn die Rückkehr vom Interrupt erfolgt ist. Die Rückkehr vom Systemaufruf ist in Datei vorhanden
entry_64.S
.Es wird die Funktion int_signal aufgerufen, von
entry_64.S
der aus die Funktion aufgerufen wirddo_notify_resume()
.Lassen Sie uns die Funktion überprüfen
do_notify_resume()
. Diese Funktion prüft, ob wir dasTIF_SIGPENDING
Flag gesetzt haben intask_struct
:SYSTEM ruft und signalisiert
"Langsame" Systemaufrufe, z. B. Blockieren von Lese- / Schreibvorgängen, Versetzen von Prozessen in den Wartezustand:
TASK_INTERRUPTIBLE
oderTASK_UNINTERRUPTIBLE
.Ein Task im Status
TASK_INTERRUPTIBLE
wirdTASK_RUNNING
durch ein Signal in den Status geändert .TASK_RUNNING
bedeutet, dass ein Prozess geplant werden kann.Wenn es ausgeführt wird, wird sein Signalhandler ausgeführt, bevor der langsame Systemaufruf abgeschlossen ist. Der
syscall
wird standardmäßig nicht abgeschlossen.Wenn das
SA_RESTART
Flag gesetzt ist,syscall
wird es nach Abschluss des Signalhandlers neu gestartet.Verweise
quelle
kill
Befehl verwendet, der eine eingebaute Shell ist). (2c) Während Semikolons nach dem Schließen}
einer Funktion streng genommen keine Fehler darstellen, sind sie unnötig und höchst unorthodox. (3) Selbst wenn alles richtig wäre, wäre es keine sehr gute Antwort auf die Frage. (3a) Die Frage ist zwar etwas unklar, scheint sich aber darauf zu konzentrieren, wie Akteure (Benutzer und Prozesse ) Signale initiieren (dh senden ). … (Fortsetzung)