
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.sh
wird 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 trap
Befehl 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 trap
Befehl 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 scriptb
Prozess so eingestellt, dass er Interrupts ignoriert und beendet. Und daher schlägt der trap
Befehl scriptb.sh
stillschweigend 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 scripta
hat blockiert INT
und CHLD
Signale; scriptb
erbt diese Einstellungen durch die fork
(hier genannt clone
). Und was scriptb
macht 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 INT
Signale zunächst die Standardbehandlung erhalten und dann ignoriert werden. scriptb
Die direkte Ausführung von der Shell unter strace
zeigt 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 scripta
und scriptb
das setzt SIGINT
den 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 bash
es sich so verhält. Vielleicht sollten Sie ihnen den Fehler melden. Der Code sig.c
sieht 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.