Wie kann ich bei einem gegebenen Shell-Prozess (zB sh
) und seinem untergeordneten Prozess (zB cat
) das Verhalten von Ctrl+ Cmithilfe der Prozess-ID der Shell simulieren ?
Das habe ich versucht:
Laufen sh
und dann cat
:
[user@host ~]$ sh
sh-4.3$ cat
test
test
Senden SIGINT
an cat
von einem anderen Terminal:
[user@host ~]$ kill -SIGINT $PID_OF_CAT
cat
empfing das Signal und beendete (wie erwartet).
Das Senden des Signals an den übergeordneten Prozess scheint nicht zu funktionieren. Warum wird das Signal nicht weitergegeben, cat
wenn es an den übergeordneten Prozess gesendet wird sh
?
Das funktioniert nicht:
[user@host ~]$ kill -SIGINT $PID_OF_SH
Antworten:
Wie CTRL+ Cfunktioniert
Das Erste ist zu verstehen, wie CTRL+ Cfunktioniert.
Wenn Sie CTRL+ drücken C, sendet Ihr Terminalemulator ein ETX-Zeichen (Textende / 0x03).
Das TTY ist so konfiguriert, dass beim Empfang dieses Zeichens ein SIGINT an die Vordergrundprozessgruppe des Terminals gesendet wird. Diese Konfiguration kann durch Ausführen
stty
und Betrachten angezeigt werdenintr = ^C;
. Die POSIX-Spezifikation besagt, dass beim Empfang von INTR ein SIGINT an die Vordergrundprozessgruppe dieses Terminals gesendet werden soll.Was ist die Vordergrundprozessgruppe?
Nun stellt sich die Frage, wie Sie die Vordergrundprozessgruppe bestimmen. Die Vordergrundprozessgruppe ist einfach die Gruppe von Prozessen, die alle von der Tastatur erzeugten Signale empfangen (SIGTSTOP, SIGINT usw.).
Die einfachste Methode zum Ermitteln der Prozessgruppen-ID ist die Verwendung von
ps
:Die zweite Spalte ist die Prozessgruppen-ID.
Wie sende ich ein Signal an die Prozessgruppe?
Nachdem wir die Prozessgruppen-ID kennen, müssen wir das POSIX-Verhalten beim Senden eines Signals an die gesamte Gruppe simulieren.
Dies können Sie tun,
kill
indem Sie-
der Gruppen-ID ein voranstellen.Wenn Ihre Prozessgruppen-ID beispielsweise 1234 lautet, würden Sie Folgendes verwenden:
Simulieren Sie CTRL+ Cmit der Terminalnummer.
In diesem Abschnitt wird beschrieben, wie Sie CTRL+ Cals manuellen Prozess simulieren . Aber was ist, wenn Sie die TTY-Nummer kennen und CTRL+ Cfür dieses Terminal simulieren möchten ?
Das wird sehr einfach.
Nehmen wir an, es
$tty
ist das Terminal, auf das Sie abzielen möchten (Sie können dies erreichen, indem Sie estty | sed 's#^/dev/##'
im Terminal ausführen).Dadurch wird ein SIGINT an die Vordergrundprozessgruppe gesendet
$tty
.quelle
kill
Befehl möglicherweise fehlschlägt.sends a SIGINT to the foreground process group of the terminal.
fork
. Minimales lauffähiges C-Beispiel unter: unix.stackexchange.com/a/465112/32558Wie vinc17 sagt, gibt es dafür keinen Grund. Wenn Sie eine signalerzeugende Tastenfolge eingeben (z. B. Ctrl+ C), wird das Signal an alle Prozesse gesendet, die mit dem Terminal verbunden sind. Es gibt keinen solchen Mechanismus für Signale, die von erzeugt werden
kill
.Allerdings mag ein Befehl
sendet das Signal an alle Prozesse in Prozessgruppe 12345; siehe kill (1) und kill (2) . Untergeordnete Elemente einer Shell befinden sich normalerweise in der Prozessgruppe der Shell (zumindest, wenn sie nicht asynchron sind). Wenn Sie also das Signal an die negative PID der Shell senden, können Sie die gewünschten Aktionen ausführen.
Hoppla
Wie vinc17 betont, funktioniert dies nicht für interaktive Shells. Hier ist eine Alternative, die funktionieren könnte :
ps -pPID_of_shell
Ruft Prozessinformationen für die Shell ab.o tpgid=
Weistps
an, nur die Terminalprozessgruppen-ID ohne Header auszugeben. Wenn dies weniger als 10000 ist,ps
wird es mit führenden Leerzeichen angezeigt. das$(echo …)
ist ein schneller Trick (und nachlauf) Räume abzustreifen führt.Ich brachte dies dazu, in flüchtigen Tests auf einem Debian-Rechner zu arbeiten.
quelle
Die Frage enthält eine eigene Antwort. Das Senden der
SIGINT
an dencat
Prozess mitkill
ist eine perfekte Simulation dessen, was passiert, wenn Sie drücken^C
.Genauer gesagt wird das Interrupt-Zeichen (
^C
standardmäßig)SIGINT
an jeden Prozess in der Vordergrundprozessgruppe des Terminals gesendet. Wenncat
Sie keinen komplizierten Befehl mit mehreren Prozessen ausführen würden, müssten Sie die Prozessgruppe beenden, um den gleichen Effekt wie zu erzielen^C
.Wenn Sie einen externen Befehl ohne den
&
Hintergrundoperator ausführen , erstellt die Shell eine neue Prozessgruppe für den Befehl und benachrichtigt das Terminal, dass diese Prozessgruppe jetzt im Vordergrund steht. Die Shell befindet sich noch in einer eigenen Prozessgruppe, die nicht mehr im Vordergrund steht. Dann wartet die Shell darauf, dass der Befehl beendet wird.An dieser Stelle scheinen Sie durch ein häufiges Missverständnis zum Opfer gefallen zu sein: Die Idee, dass die Shell etwas unternimmt, um die Interaktion zwischen ihren untergeordneten Prozessen und dem Terminal zu erleichtern. Das stimmt einfach nicht. Sobald das Setup abgeschlossen ist (Prozesserstellung, Terminalmoduseinstellung, Erstellen von Pipes und Umleiten anderer Dateideskriptoren sowie Ausführen des Zielprogramms), wartet die Shell nur noch . Was Sie
cat
eingeben, durchläuft nicht die Shell, egal ob es sich um eine normale Eingabe oder ein signalerzeugendes Sonderzeichen handelt^C
. Dercat
Prozess hat direkten Zugriff auf das Terminal über seine eigenen Dateideskriptoren, und das Terminal kann Signale direkt an dencat
Prozess senden , da dies die Vordergrundprozessgruppe ist.Nachdem der
cat
Prozess beendet ist, wird die Shell benachrichtigt, da sie der übergeordnetecat
Prozess ist. Dann wird die Shell aktiv und rückt sich wieder in den Vordergrund.Hier ist eine Übung, um Ihr Verständnis zu verbessern.
Führen Sie an der Shell-Eingabeaufforderung in einem neuen Terminal den folgenden Befehl aus:
Das
exec
Schlüsselwort bewirkt, dass die Shell ausgeführt wird,cat
ohne einen untergeordneten Prozess zu erstellen. Die Schale wird durch ersetztcat
. Die PID, die früher zur Shell gehörte, ist jetzt die PID voncat
. Überprüfen Sie dies mitps
in einem anderen Terminal. Geben Sie einige zufällige Zeilen ein und stellencat
Sie fest, dass sie sich normal verhalten, obwohl Sie keinen Shell-Prozess als übergeordnetes Element haben. Was passiert, wenn Sie^C
jetzt drücken ?Antworten:
quelle
exec cat
Pressen^C
nicht einfach^C
in Katze landen würde . Warum sollte er das beenden,cat
was jetzt die Shell ersetzt hat? Da die Shell ersetzt wurde, implementiert die Shell die Logik, SIGINT beim Empfang an ihre Kinder zu senden^C
.Es gibt keinen Grund, das
SIGINT
an das Kind weiterzugeben. Darüber hinaus heißt es in dersystem()
POSIX-Spezifikation: "Die system () -Funktion soll die Signale SIGINT und SIGQUIT ignorieren und das Signal SIGCHLD blockieren, während auf die Beendigung des Befehls gewartet wird."Wenn die Shell den Empfang
SIGINT
weiterleitet, z. B. nach einer echten Strg-C-Anweisung, würde dies bedeuten, dass der untergeordnete Prozess dasSIGINT
Signal zweimal empfängt , was zu unerwünschtem Verhalten führen kann.quelle
system()
. Aber Sie haben Recht, wenn es das Signal empfängt (offensichtlich auch), gibt es keinen Grund, es nach unten zu verbreiten.setpgid
POSIX C-Prozessgruppen-MinimalbeispielEin minimal ausführbares Beispiel für die zugrunde liegende API könnte das Verständnis erleichtern.
Dies zeigt, wie das Signal an das untergeordnete Element gesendet wird, wenn das untergeordnete Element seine Prozessgruppe nicht mit geändert hat
setpgid
.Haupt c
GitHub Upstream .
Kompilieren mit:
Laufen Sie ohne
setpgid
Ohne CLI-Argumente
setpgid
wird Folgendes nicht ausgeführt:Mögliches Ergebnis:
und das Programm hängt.
Wie wir sehen können, ist die pgid beider Prozesse dieselbe, da sie vererbt wird
fork
.Dann, wann immer Sie schlagen:
Es gibt wieder aus:
Dies zeigt, wie:
kill(-pgid, SIGINT)
Beenden Sie das Programm, indem Sie an beide Prozesse ein anderes Signal senden, z
Ctrl + \
. B. SIGQUIT mit .Laufen Sie mit
setpgid
Wenn Sie mit einem Argument laufen, zB:
dann ändert das Kind seine pgid, und jetzt wird jedes Mal nur ein einziges Zeichen vom Elternteil gedruckt:
Und jetzt, wann immer Sie schlagen:
Nur der Elternteil empfängt das Signal ebenfalls:
Sie können die Eltern weiterhin wie bisher mit einem SIGQUIT töten:
Das Kind hat jetzt jedoch eine andere PGID und empfängt dieses Signal nicht! Dies ist ersichtlich aus:
Du musst es explizit töten mit:
Dies macht deutlich, warum Signalgruppen existieren: Andernfalls würden wir eine Reihe von Prozessen übrig haben, die ständig manuell gereinigt werden müssten.
Getestet unter Ubuntu 18.04.
quelle