Ist es möglich, ein Programm, das Signale ignoriert, mit Strg-C zum Beenden zu zwingen?

Ist es möglich, ein Programm, das Signale ignoriert, mit Strg-C zum Beenden zu zwingen?

Ich habe ein Programm, das SIGINT ignoriert, das ich aber im Vordergrund ausführen möchte. Ich würde gerne einen Weg finden, es mit Strg+C zu schließen. Gibt es eine Möglichkeit, einen Wrapper (den Sie aufrufen würden ./wrapper.sh my_program) zu schreiben, der das fehlerhafte Programm zum Beenden zwingt, möglicherweise indem er das ignorierte SIGINT erkennt und ein SIGKILL generiert?

Diese Antwortist das genaue Gegenteil von dem, was ich suche – ich möchte ein Programm, das ein Signal ignoriert, zum Beenden bei SIGINT zwingen.

Antwort1

Erstellen wir einen Wrapper, der SIGINT in SIGKILL „konvertiert“.

Wir benötigen einen Prozess, der mitläuft my_programund SIGINT bei Ctrl+ erhält c. Wenn er SIGINT empfängt, sollte er SIGKILL an senden my_program. Beachten Sie, dass wir nicht my_programwirklich SIGINT empfangen müssen, das durch Ctrl+ verursacht wird c; es reicht, wenn es SIGKILL durch den zusätzlichen Prozess erhält.

Relevante Fakten:

  • Eine Standardmethode, um einen Prozess parallel zu einem anderen laufen zu lassen, besteht darin, einen der beiden asynchron im Hintergrund laufen zu lassen (d. h. mit Beendigung &).
  • Wenn Sie Ctrl+ drücken c, sendet Ihr Terminalemulator SIGINT an die Prozesse in der Vordergrundprozessgruppe.
  • Einige (einfache) Shells führen alles in derselben Prozessgruppe aus. Wenn sie angewiesen werden, einen Befehl im Hintergrund auszuführen, leiten sie dessen Standardeingabe /dev/nulloder eine entsprechende Datei um, um zu verhindern, dass der Befehl Eingaben stiehlt.
  • Andere Shells können jeden Befehl in einer separaten Prozessgruppe ausführen. Wenn sie angewiesen werden, einen Befehl im Hintergrund auszuführen, lassen sie dessen Standardeingabe unverändert; dennoch kann der Befehl im Hintergrund keine Eingaben vom steuernden Terminal stehlen, daSIGTTIN. Dadurch kann die Shell einen Job vom Hintergrund in den Vordergrund verschieben, indem sie das Terminal über die neue Vordergrundprozessgruppe informiert. Der Mechanismus heißt Jobsteuerung und kann deaktiviert werden. In Skripten ist er standardmäßig deaktiviert.
  • In beiden Fällen kann ein Prozess im Hintergrund nicht vom Terminal lesen. Das bedeutet, dass wir nicht my_programim Hintergrund laufen sollten, wenn die Standardeingabe das Terminal ist und my_programvon diesem gelesen werden muss.
  • Leider sollten wir in einigen Shells den anderen Prozess auch nicht im Hintergrund laufen lassen. Einige Shells verwenden separate Prozessgruppen, selbst wenn die Jobsteuerung deaktiviert ist. Der andere Prozess, der sich nicht in der Vordergrundprozessgruppe befindet, erhält bei Ctrl+ kein SIGINT cund kann es daher nicht in SIGKILL „konvertieren“.
  • In meinem Debian 10 poshgibt es eine Shell, die alles in derselben Prozessgruppe ausführt.

Dies führt zu folgendem Wrapper:

#!/usr/bin/env posh

( trap 'kill -s KILL 0' INT
  while kill -s 0 "$$" 2>/dev/null; do sleep 1; done
) &
exec "$@"

exec "$@"läuft my_program(oder was auch immer Sie angeben), möglicherweise mit Argumenten. Dank exec my_programwird der Wrapper ersetzt. Er kann nicht nur vom Standard-DIN lesen, das ein Terminal sein kann; seine PID ist die des ersetzten Wrappers. In diesem Sinne ist der Wrapper transparent. Darüber hinaus hat dies eine nette Funktion: Die PID von my_programist vor dem Start bekannt my_programund wir können sie problemlos im anderen Prozess verwenden (in diesem Fall in einer Subshell im Hintergrund), um festzustellen, wann my_programer tatsächlich beendet wird.

Das trap"wandelt" SIGINT in SIGKILL um. Beachten Sie, kill -s KILL 0dass SIGKILL an die gesamte Prozessgruppe gesendet wird, einschließlich der untergeordneten Prozesse, my_programwenn sie in der Gruppe sind (wenn Sie nur beenden möchten, my_programverwenden Sie kill -s KILL "$$"stattdessen). kill -s 0 "$$"Testet dennoch nur die Existenz von my_program.

Es gibt eine Alternative, die keine Shell erfordert und alles in derselben Prozessgruppe ausführt. Der Trick ist: Prozesse in einer Pipeline sollten in einer Prozessgruppe ausgeführt werden. Durch geschicktes Umleiten können Sie eine Pipeline erstellen, bei der die Teile nicht miteinander verbunden sind.

#!/bin/sh -

exec 9>&1
( "$@"; kill -s TERM 0 ) >&9 9>&- | (
  trap 'kill -s KILL 0' INT
  while :; do sleep 1; done
)

In dieser Variante my_programwird der Wrapper nicht ersetzt. Die zweite Möglichkeit killbesteht darin, SIGINT in SIGKILL zu „konvertieren“. Die erste Möglichkeit killbesteht darin, die Schleife zu beenden, wenn my_programsie unter Umständen beendet wird, in denen die Schleife ansonsten bestehen bleiben würde.

Es gibt einige Szenarien, die möglicherweise nicht wie erwartet funktionieren (mit jedem Wrapper). Dazu gehören:

  • Wenn my_programes sich verzweigt und beendet, wird die eigentliche Aufgabe dem Kind überlassen. Das betreffende problematische Programm verhält sich höchstwahrscheinlich nicht so, da Sie versucht haben, es zu Ctrl+ c. Aber im Allgemeinen könnte es so sein.
  • Wenn my_programin einer oder mehreren anderen Prozessgruppen untergeordnete Prozesse erzeugt werden und Sie diese zusammen mit den übergeordneten Prozessen beenden möchten.
  • Es konfiguriert das Terminal so, dass es bei + my_programkein SIGINT sendet . Wenn Sie vermuten, dass dies passiert, verbessern Sie den Wrapper, indem Sie zwischen und setzen . In diesem Fall ignoriert das Programm SIGINT möglicherweise nicht einmal, sondern stellt einfach sicher, dass es es nicht vom Terminal erhält. Vielleicht ist SIGKILL also übertrieben; vielleicht reicht es aus, diese Funktionalität des Terminals wiederherzustellen, und + wird funktionieren.Ctrlcstty -F /dev/tty intr ^Csleep 1doneCtrlc

Aus einem Kommentar:

Programme, die SIGINT ignorieren, tun dies normalerweise aus einem sehr guten Grund.

Stimmt. Versuchen Sie SIGTERM oder SIGHUP statt SIGKILL. Vielleicht ignoriert das betreffende Programm mindestens eines davon nicht und wird mit der richtigen Bereinigung ordnungsgemäß beendet.

verwandte Informationen