Seltsames Problem mit Trap und SIGINT

Seltsames Problem mit Trap und SIGINT

Bitte erläutern Sie dies:

#!/bin/bash
# This is scripta.sh
./scriptb.sh &
pid=$!
echo $pid started
sleep 3
while true
do
    kill -SIGINT $pid
    echo scripta.sh $$
    sleep 3
done 

-

#!/bin/bash
# This is scriptb.sh
trap "echo Ouch;" SIGINT

while true
do
 echo scriptb.sh $$
 sleep 1
done

Wenn ich es ausführe, ./scripta.shwird der Trap nicht gedruckt. Wenn ich von SIGINT zu einem anderen Signal wechsle (ich habe SIGTERM, SIGUSR1 ausprobiert), druckt der Trap wie erwartet „Autsch“. Wie kann das passieren?

Antwort1

Wenn ich von SIGINT zu einem anderen Signal wechsle (ich habe SIGTERM, SIGUSR1 ausprobiert), druckt die Falle wie erwartet „Autsch“.

Anscheinend haben Sie SIGQUIT nicht ausprobiert; Sie werden wahrscheinlich feststellen, dass es sich genauso verhält wie SIGINT.

Das Problem ist die Jobkontrolle.

In den frühen Tagen von Unix stellte die Shell immer dann, wenn sie einen Prozess oder eine Pipeline in den Hintergrund stellte, diese Prozesse so ein, dass sie SIGINT und SIGQUIT ignorierten, damit sie nicht beendet würden, wenn der Benutzer anschließend Ctrl+ C(Unterbrechen) oder Ctrl+ \(Beenden) für einen Vordergrund-Task eingab. Als die Job-Steuerung aufkam, brachte sie Prozessgruppen mit sich, und so muss die Shell jetzt nur noch den Hintergrund-Job in eine neue Prozessgruppe setzen; solange dies nicht die aktuelle Terminal-Prozessgruppe ist, sehen die Prozesse keine Signale von der Tastatur ( Ctrl+ C, Ctrl+ \ und Ctrl+ Z(SIGTSTP)). Die Shell kann Hintergrundprozesse mit der Standardsignalverteilung belassen. Tatsächlich muss sie das wahrscheinlich tun, damit die Prozesse mit Ctrl+ beendet werden können C, wenn sie in den Vordergrund gebracht werden.

Nicht interaktive Shells verwenden jedoch keine Jobsteuerung. Aus historischen Gründen ist es sinnvoll, dass eine nicht interaktive Shell auf das alte Verhalten zurückgreift und SIGINT und SIGQUIT für Hintergrundprozesse ignoriert – damit die Hintergrundprozesse weiter ausgeführt werden können, selbst wenn ihnen Tastatursignale gesendet werden. Und Shell-Skripte werden in nicht interaktiven Shells ausgeführt.

Und wenn Sie den letzten Absatz unter dem trapBefehl inbash(1), du wirst sehen

Beim Eintritt in die Shell ignorierte Signale können nicht abgefangen oder zurückgesetzt werden.

Wenn du also ./scriptb.sh & vor deineminteraktivShell-Eingabeaufforderung, ihre Signaldispositionen bleiben unverändert (auch wenn sie in den Hintergrund gestellt werden) und der trapBefehl funktioniert wie erwartet. Aber wenn Sie ausführen ./scripta.sh(mit oder ohne &), wird das Skript in einer nicht interaktiven Shell ausgeführt. Und wenn diese nicht interaktive Shell ausgeführt wird ./scriptb.sh &, wird der scriptbProzess so eingestellt, dass er Interrupts ignoriert und beendet. Und daher schlägt der trapBefehl scriptb.shstillschweigend fehl.

Antwort2

Mit einigen Nachverfolgungen:

strace -o aaa ./scripta

Wir können beobachten, dass standardmäßig

read(255, "#!/bin/bash\n# this is scripta.sh"..., 153) = 153
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
rt_sigprocmask(SIG_BLOCK, [INT CHLD], [], 8) = 0
lseek(255, -108, SEEK_CUR)              = 45
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f1ee9b7ca10) = 2483

also scriptahat blockiert INTund CHLDSignale; scriptberbt diese Einstellungen durch die fork(hier genannt clone). Und was scriptbmacht es? Wenn wir es von über ausführen scripta:

strace -o bbb ./scriptb &

Und wenn wir dann nach signalbezogenen Dingen suchen, finden wir:

rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7fb1b2e75250}, {SIG_IGN, [], 0}, 8) = 0
rt_sigaction(SIGINT, {SIG_IGN, [], SA_RESTORER, 0x7fb1b2e75250}, {SIG_DFL, [], SA_RESTORER, 0x7fb1b2e75250}, 8) = 0

Das zeigt an, dass nichts blockiert wird und dass INTSignale zunächst die Standardbehandlung erhalten und dann ignoriert werden. scriptbDie direkte Ausführung von der Shell unter stracezeigt dagegen:

rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f2c3f6d4250}, {SIG_DFL, [], 0}, 8) = 0
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f2c3f6d4250}, {SIG_DFL, [], SA_RESTORER, 0x7f2c3f6d4250}, 8) = 0
rt_sigprocmask(SIG_BLOCK, [INT], [], 8) = 0
rt_sigaction(SIGINT, {0x45fbf0, [], SA_RESTORER, 0x7f2c3f6d4250}, {SIG_DFL, [], SA_RESTORER, 0x7f2c3f6d4250}, 8) = 0
rt_sigprocmask(SIG_BLOCK, [INT CHLD], [], 8) = 0
rt_sigprocmask(SIG_BLOCK, ~[RTMIN RT_1], [INT CHLD], 8) = 0
...

Oder nie ignoriert, bevor man in die dann wiederholte Sleep-Call-Behandlung einsteigt. Okay. Äh. Lass uns einen Shim dazwischen setzen scriptaund scriptbdas setzt SIGINTden Standardzustand zurück...

#include <getopt.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    int ch;
    while ((ch = getopt(argc, argv, "h?")) != -1) {
        switch (ch) {
        case 'h':
        case '?':
        default:
            abort();
        }
    }
    argc -= optind;
    argv += optind;
    if (argc < 1) abort();

    signal(SIGINT, SIG_DFL);

    execvp(*argv, argv);
    abort();
    return 1;
}

Wird wie folgt verwendet:

$ make defaultsig
cc     defaultsig.c   -o defaultsig
$ grep defaultsig scripta.sh
./defaultsig ./scriptb.sh &
$ ./scripta.sh 
12510 started
scriptb.sh 12510
scriptb.sh 12510
scriptb.sh 12510
scripta.sh 12509
Ouch
scriptb.sh 12510
scriptb.sh 12510
scriptb.sh 12510
scripta.sh 12509
Ouch
...

Ja, funktioniert jetzt gut. Ich weiß jedoch nicht, warum bashes sich so verhält. Vielleicht sollten Sie ihnen den Fehler melden. Der Code sig.csieht ziemlich kompliziert aus und es gibt anderswo mehr Signalverarbeitung ...

Antwort3

Seltsames Problem mit Trap und SIGINT

Vielen Dank an alle für die Antworten und für die Zeit, die Sie sich für das Studium des Problems genommen haben.

Gestatten Sie mir bitte, alles zusammenzufassen und zu integrieren (mit der Entschuldigung für das, was im Folgenden offensichtlich sein könnte):

1) Ich habe vergessen, in meiner Frage hinzuzufügen, dass ich auch SIGQUIT ausprobiert habe und es sich wie SIGINT verhalten hat;

2) Daraus schloss ich bereits, dass das Problem mit der Standardeinstellung einer interaktiven Bash für diese beiden Signale zusammenhing.

3) Ihre Standardaktion wird bei der Interaktion mit Bash nicht ausgeführt, da es keinen Sinn ergibt, etwas zu beenden oder zu unterbrechen, wenn Sie nur die Eingabeaufforderung haben. Wenn Sie die Shell verlassen möchten, geben Sie einfach exit ein.

4) Ich glaube nicht, dass SIGQUIT und SIGINT eine besondere Rolle bei der Jobsteuerung spielen (im Gegensatz zu SIGTSTP, SIGTTOU, SIGTTIN);

5) Es macht für mich keinen Sinn, dass die Standarddisposition einer interaktiven Bash für diese beiden Signale von einer Hintergrund-Shell (nicht interaktiv) übernommen wird (in unserem Fall die, die scriptb.sh ausführt).

6) Da die Vordergrundprozessgruppe die Dispositionen für SIGQUIT und SIGINT nicht (von der Shell, die sie gestartet hat) erbt, sollte es meiner Meinung nach sinnvoll sein, dass dasselbe mit den Hintergrundprozessgruppen geschieht.

7) Darüber hinaus sollte Trap die vererbte Veranlagung ändern, wie auch immer sie aussieht.

8) Alles in allem neige ich dazu, thrig zuzustimmen und zu denken, dass es sich bei dem, was wir hier sehen, um einen Fehler handelt.

verwandte Informationen