Steuern Sie, welcher Vorgang mit Strg + C abgebrochen wird

13

Ich habe eine Live-CD, die unter Linux startet und ein kleines Bash-Skript ausführt. Das Skript sucht nach einem zweiten Programm und führt es aus (normalerweise eine kompilierte C ++ - Binärdatei).

Sie sollten in der Lage sein, das zweite Programm abzubrechen, indem Sie Ctrl+ drücken C. Was passieren sollte , ist, dass das zweite Programm anhält und das Bash-Skript die Bereinigung fortsetzt. Was tatsächlich passiert, ist, dass sowohl die Hauptanwendung als auch das Bash-Skript beendet werden. Welches ist ein Problem.

Also habe ich das trapBuiltin benutzt, um Bash anzuweisen, SIGINT zu ignorieren. Und jetzt beendet Ctrl+ Cdie C ++ - Anwendung, aber Bash setzt die Ausführung fort. Groß.

Oh ja ... Manchmal ist die "zweite Anwendung" ein anderes Bash-Skript. Und in diesem Fall macht Ctrl+ Cjetzt überhaupt nichts mehr .

Klar ist mein Verständnis, wie dieses Zeug funktioniert, falsch ... Wie steuere ich, welcher Prozess SIGINT erhält, wenn der Benutzer Ctrl+ drückt C? Ich möchte dieses Signal nur auf einen bestimmten Prozess lenken .

MathematicalOrchid
quelle

Antworten:

11

Nach vielen, vielen Stunden im Internet habe ich die Antwort gefunden.

  1. Linux hat den Begriff einer Prozessgruppe .

  2. Der TTY-Treiber hat den Begriff "Vordergrundprozessgruppe".

  3. Wenn Sie Ctrl+ drücken C, sendet das TTY SIGINTan jeden Prozess in der Vordergrundprozessgruppe. (Siehe auch diesen Blogeintrag .)

Aus diesem Grund werden sowohl die kompilierte Binärdatei als auch das Skript, mit dem sie gestartet wird, blockiert. Tatsächlich möchte ich nur, dass die Hauptanwendung dieses Signal empfängt, nicht die Startskripte.

Die Lösung ist jetzt offensichtlich: Wir müssen die Anwendung in eine neue Prozessgruppe einordnen und sie zur Vordergrund-Prozessgruppe für dieses TTY machen. Anscheinend ist der Befehl dazu

setsid -c <applcation>

Und das ist alles. Wenn der Benutzer jetzt Ctrl+ drückt C, wird SIGINT an die Anwendung (und an alle Kinder, die möglicherweise vorhanden sind) und an niemanden anderen gesendet. Welches ist, was ich wollte.

  • setsid für sich genommen ordnet die Anwendung einer neuen Prozessgruppe zu (in der Tat einer neuen "Sitzung", bei der es sich anscheinend um eine Gruppe von Prozessgruppen handelt).

  • Durch Hinzufügen des -cFlags wird diese neue Prozessgruppe zur "Vordergrund" -Prozessgruppe für das aktuelle TTY. (Dh es wird, SIGINTwenn Sie Ctrl+ drücken C)

Ich habe viele widersprüchliche Informationen darüber gesehen, wann Bash Prozesse in einer neuen Prozessgruppe ausführt oder nicht. (Insbesondere scheint es für "interaktive" und "nicht interaktive" Shells unterschiedlich zu sein.) Ich habe Vorschläge gesehen, wie Sie dies möglicherweise mit cleveren Pipe-Tricks zum Laufen bringen können ... Ich weiß nicht. Aber der obige Ansatz scheint für mich zu funktionieren.

MathematicalOrchid
quelle
5
Sie haben es fast geschafft ... Die Auftragssteuerung ist beim Ausführen eines Skripts standardmäßig deaktiviert, Sie können sie jedoch mit aktivieren set -m. Es ist ein bisschen sauberer und einfacher als setsidjedes Mal, wenn Sie ein Kind führen.
Psusi
@psusi Danke für den Tipp! Ich muss nur ein Kind führen, es ist also keine große Sache. Ich weiß jetzt, wo ich im Bash-Handbuch nachschauen muss ...
MathematicalOrchid
Ironischerweise habe ich das umgekehrte Problem, bei dem ich möchte, dass die Eltern Sigint fangen, aber das liegt nicht an "cleveren Pfeifentricks". Auch Fortschrittsgruppe -> Prozessgruppe
Andrew Domaszek
2

Wie im Kommentar zu f01 erwähnt, sollten Sie SIGTERM an den untergeordneten Prozess senden. In den folgenden Skripten wird gezeigt, wie ^ C abgefangen und ein Signal an einen untergeordneten Prozess gesendet wird.

Erstens die Eltern.

traptest

#!/bin/bash

# trap test
# Written by PM 2Ring 2014.10.23

myname=$(basename "$0")
child=sleeploop

set_trap()
{
    sig=$1
    msg="echo -e \"\n$myname received ^C, sending $sig to $child, $pid\""
    trap "$msg; kill -s $sig $pid" SIGINT
}
trap "echo \"bye from $myname\"" EXIT

echo "running $child..."
./$child 5  &
pid=$!

# set_trap SIGINT
set_trap SIGTERM
echo "$child pid = $pid"

wait $pid
echo "$myname finished waiting"

Und jetzt das Kind.

Schlaflöffel

#!/bin/bash

# child script for traptest
# Written by PM 2Ring 2014.10.23

myname=$(basename "$0")
delay="$1"

set_trap()
{
    sig=$1
    trap "echo -e '\n$myname received $sig signal';exit 0" $sig
}

trap "echo \"bye from $myname\"" EXIT
set_trap SIGTERM
set_trap SIGINT

#Select sleep mode
if false
then
    echo "Using foreground sleep"
    Sleep()
    {
        sleep $delay
    }
else
    echo "Using background sleep"
    Sleep()
    {
        sleep "$delay" &
        wait $!
    }
fi

#Time to snooze :)
for ((i=0; i<5; i++));
do
    echo "$i: sleeping for $delay"
    Sleep
done

echo "$myname terminated normally"

Wenn Traptest SIGTERM sendet, verhalten sich die Dinge gut, aber wenn Traptest SIGINT sendet, sieht es Sleeploop nie.

Wenn sleeploop SIGTERM abfängt und der Schlafmodus im Vordergrund ist, kann es nicht auf das Signal reagieren, bis es aus dem aktuellen Schlafzustand aufwacht. Wenn der Schlafmodus jedoch im Hintergrund ist, reagiert er sofort.

PM 2Ring
quelle
Vielen Dank für dieses großartige Beispiel, es hat mir sehr geholfen zu verstehen, wie ich mein Skript verbessern kann :)
TabeaKischka
2

In Ihrem einleitenden Bash-Skript.

  • Verfolgen Sie die PID des zweiten Programms

  • Nimm den ZEICHEN

  • Wenn Sie ein SIGINT abgefangen haben, senden Sie ein SIGINT an die zweite Programm-PID

f01
quelle
1
Das kann nicht helfen. Wenn Sie SIGINT im übergeordneten Skript abfangen, können die untergeordneten Skripts SIGINT nicht empfangen, unabhängig davon, ob Sie es vom übergeordneten oder von einer anderen Shell senden. Das ist aber nicht so schlimm, wie es sich anhört, denn Sie können SIGTERM an das Kind senden, was anscheinend sowieso das bevorzugte Signal ist.
PM 2Ring
1
Warum wird SIGTERM "bevorzugt"?
MathematicalOrchid
Sie sind fast ähnlich. SIGINT ist das vom steuernden Terminal / Benutzer gesendete Signal, z. B. Strg + C. SIGTERM können Sie auch senden, wenn der Vorgang abgebrochen werden soll. Weitere Gedanken hier en.wikipedia.org/wiki/Unix_signal
f01