GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Wann wird ein Signal behandelt und warum frieren einige Informationen ein?

termA friert nicht ein. Es zeigt nur den Rückruf im Eingabefeld an. Einfach Enter drücken Taste um mit der Eingabe fortzufahren.


Bevor Sie Ihr Problem erläutern, ein wenig Kontext dazu, wie read Befehl funktioniert. Es liest Eingabedaten von stdin ein bis EOF angetroffen wird. Es ist sicher, den Anruf zu read zu sagen Der Befehl blockiert nicht, wenn es um das Lesen von Dateien auf der Festplatte geht. Aber wenn stdin mit dem Terminal verbunden ist, blockiert der Befehl, bis der Benutzer etwas eingibt.

Wie funktionieren Signalhandler?

Eine einfache Erklärung, wie die Signalverarbeitung funktioniert. Siehe das folgende Snippet in C die nur auf SIGINT wirkt (auch bekannt als CTRL+C )

#include <stdio.h>
#include <signal.h>

/* signal handler definition */
void signal_handler(int signum){
  printf("Hello World!\n");
}

int main(){
  //Handle SIGINT with a signal handler
  signal(SIGINT, signal_handler);
  //loop forever!
  while(1);
}

Es registriert den Signalhandler und tritt dann in die Endlosschleife ein. Wenn wir Ctrl-C treffen , wir sind uns alle einig, dass der Signalhandler signal_handler() ausführen soll und "Hello World!" druckt auf dem Bildschirm, aber das Programm befand sich in einer Endlosschleife. Um "Hello World!" zu drucken es muss so gewesen sein, dass es die Schleife unterbrochen hat, um den Signal-Handler auszuführen, richtig? Es sollte also sowohl die Schleife als auch das Programm verlassen. Mal sehen:

gcc -Wall -o sighdl.o signal.c
./sighdl.o 
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!

Wie die Ausgabe zeigt, haben wir jedes Mal Ctrl-C ausgegeben , "Hello World!" wird gedruckt, aber das Programm kehrt zur Endlosschleife zurück. Es ist nur nach Ausgabe eines SIGQUIT Signal mit Ctrl-\ wurde das Programm tatsächlich beendet. Abhängig von Ihrem ulimit Einstellungen, würde es einen Kern ausgeben oder die empfangene Signalnummer ausdrucken.

Während die Interpretation, dass die Schleife verlassen würde, vernünftig ist, berücksichtigt sie nicht den Hauptgrund für die Signalbehandlung, dh die asynchrone Ereignisbehandlung. Das heißt, der Signalhandler agiert aus dem Standardablauf der Programmsteuerung heraus; Tatsächlich wird das gesamte Programm in einem Kontext gespeichert, und es wird ein neuer Kontext erstellt, in dem der Signal-Handler nur ausgeführt werden kann. Sobald der Signal-Handler seine Aktionen abgeschlossen hat, wird der Kontext zurückgeschaltet und der normale Ausführungsablauf beginnt (d. h. der while(1) ).

Um Ihre Fragen zu beantworten,

Die Schlussfolgerung bestätigte Folgendes:Wenn bash einen externen Befehl im Vordergrund ausführt, verarbeitet es keine empfangenen Signale, bis der Vordergrundprozess beendet wird

Das Wichtigste, was hier zu beachten ist, ist das externe Befehlsteil. Im ersten Fall, wobei sleep ist ein externer Prozess, aber im zweiten Fall read ist ein eingebauter aus der Shell selbst. Die Ausbreitung des Signals zu diesen beiden unterscheidet sich also in beiden Fällen

type read
read is a shell builtin
type sleep
sleep is /usr/bin/sleep

1.Öffnen Sie ein Terminal namens termA und führen Sie die erstellte Datei callback.sh zum ersten Mal mit /bin/bash callback.sh aus, die Informationen werden sofort angezeigt.

Ja, dieses Verhalten wird erwartet. Denn zu diesem Zeitpunkt ist nur die Funktion definiert und der Trap-Handler an der Funktion myCallback registriert und das Signal wird noch nicht im Skript empfangen. Wie die Ausführungssequenz verläuft, wird die Nachricht von read wird zum ersten Mal ausgegeben.

2.Öffnen Sie ein neues Terminal namens termB und führen Sie pkill -USR1 -f callback.sh aus Beim ersten Mal erscheint die Info sofort in termA

Ja, während die read Befehl wartet auf String gefolgt von Enter Tastendruck, der den EOF signalisiert erhält er ein Signal SIGUSR1 vom anderen Terminal wird der aktuelle Ausführungskontext gespeichert und die Steuerung auf den Signalhandler umgeschaltet, der den String mit dem aktuellen Datum ausgibt.

Sobald der Handler die Ausführung beendet hat, wird der Kontext mit while fortgesetzt Schleife, in der der read Der Befehl wartet immer noch auf eine Eingabezeichenfolge. Bis zum read Befehl erfolgreich ist, würden alle nachfolgenden Signal-Traps nur die Zeichenfolge innerhalb des Signal-Handlers ausgeben.

Fahren Sie mit termB fort und führen Sie pkill -USR1 -f callback.sh aus das zweite Mal.

Wie zuvor erklärt, der read Befehl in Ihrer While-Schleife einmal nicht vollständig ist, nur wenn er erfolgreich einen String liest, würde die nächste Iteration der Schleife beginnen und eine neue Eingabeaufforderung ausgegeben werden.

Bildquelle:The Linux Programming Interface von Michael KerrisK


Die Schlussfolgerung bestätigte Folgendes:Wenn bash einen externen Befehl im Vordergrund ausführt, verarbeitet es keine empfangenen Signale, bis der Vordergrundprozess beendet wird.

bash macht behandeln die Signale bei C / Implementierungsebene, führt aber nicht die mit trap festgelegten Handler aus bis der Vordergrundprozess beendet ist. Das wird vom Standard verlangt:

When a signal for which a trap has been set is received while the shell is waiting for the completion of a utility executing a foreground command, the trap associated with that signal shall not be executed until after the foreground command has completed.

Beachten Sie, dass der Standard keinen Unterschied zwischen eingebauten und externen Befehlen macht; im Fall eines "Dienstprogramms" wie read , der nicht in einem separaten Prozess ausgeführt wird, ist nicht ersichtlich, ob ein Signal in seinem "Kontext" oder in dem der Haupt-Shell auftritt und ob ein Handler mit trap gesetzt ist sollte vor oder nach dem read ausgeführt werden zurück.

Problem 1:callback.sh enthält eine unendliche While-Schleife, wie erklärt wird, dass sie keine empfangenen Signale verarbeitet, bis der Vordergrundprozess beendet wird. In diesem Fall wird der Vordergrundprozess nie beendet.

Das ist falsch. bash führt den while nicht aus Schleife in einem separaten Prozess. Da es keinen Vordergrundprozess bash gibt wartet, kann es jeden Handler-Satz mit trap ausführen sofort. Wenn read ein externer Befehl wäre, das und nicht while wäre der Vordergrundprozess.

Ausgabe 2:Kein please input something for foo: shown in termA;

gehe zu termB, führe pkill -USR1 -f callback.sh aus das dritte Mal.

Die folgende Info wird erneut in termA angezeigt:callback function called at Mon Nov 19 09:07:24 HKT 2018

Immer noch kein please input something for foo: in termA gezeigt. Warum die Info please input something for foo: Einfrieren?

Es friert nicht ein. Drücken Sie einfach Enter und es wird wieder angezeigt.

Es ist einfach so

a) bash wird read neu starten eingebaut an Ort und Stelle wenn es durch ein Signal unterbrochen wird - es kehrt nicht zurück und geht erneut durch while Schleife

b) Es wird die mit -p eingestellte Eingabeaufforderung nicht erneut anzeigen in diesem Fall.

Beachten Sie, dass bash zeigt die Eingabeaufforderung nicht einmal erneut an, falls ein SIGINT von der Tastatur behandelt wurde, auch wenn es die bisher vom Benutzer gelesene Zeichenfolge verwirft:

$ cat goo
trap 'echo INT' INT
echo $$
read -p 'enter something: ' var
echo "you entered '$var'"

$ bash goo
24035
enter something: foo<Ctrl-C>^CINT
<Ctrl-C>^CINT
<Ctrl-C>^CINT
bar<Enter>
you entered 'bar'

Das gibt you entered 'foobar' aus wenn das Signal von einem anderen Fenster mit kill -INT <pid> gesendet wurde statt mit Strg-C vom Endgerät.

Das ganze Zeug in diesem letzten Teil (wie read unterbrochen wird usw.) ist sehr bash Spezifisch. In anderen Shells wie ksh oder dash , und sogar in bash bei Ausführung im POSIX-Modus (bash --posix ), wird jedes behandelte Signal tatsächlich den read unterbrechen eingebaut. Im obigen Beispiel gibt die Shell you entered '' aus und beenden nach dem ersten ^C, und wenn der read aus einer Schleife aufgerufen wird, wird die Schleife neu gestartet.


Linux
  1. Wer verwendet POSIX-Echtzeitsignale und warum?

  2. Wann ist setsid() nützlich oder warum müssen wir Prozesse in Linux gruppieren?

  3. Warum muss stdout explizit geleert werden, wenn es in eine Datei umgeleitet wird?

  4. Warum ruft malloc() mmap() und brk() austauschbar auf?

  5. Warum generiert Clang bei der Weiterleitung unverständlichen Text?

Was ist ein Linux-Server und warum benötigt Ihr Unternehmen einen?

Warum meldet OpenStack den Hypervisor-Typ als QEMU, wenn libvirt_type KVM ist?

Warum sind einige Emoji B&W und andere zu groß?

Warum wird diese Shell-Pipeline beendet?

Warum verwendet rsync keine Delta-Übertragung für lokale Dateien?

Wann sendet das System ein SIGTERM an einen Prozess?