Warum funktioniert SIGSTOP nicht innerhalb einer Bildschirmsitzung?

Warum funktioniert SIGSTOP nicht innerhalb einer Bildschirmsitzung?

Betrachten Sie das folgende Skript:

#!/bin/bash
OFILE='log'
echo 'ok' > ${OFILE}
kill -SIGSTOP $$
echo 'after stop' > ${OFILE}

In einer interaktiven Shell wird das Skript gestoppt und die Ausgabe ist ok. Wenn es jedoch gestartet wird als

screen -d -m ./test.sh

die Ausgabe ist after stop. Verarbeitet der Bildschirm das Signal? Wie kann ein Prozess in einer Bildschirmsitzung angehalten werden?

Antwort1

Wahrscheinlich startet der Screen-Prozess den gestoppten Bash-Prozess neu. Ich habe Folgendes versucht (SIGSTOP-Signal an beide Prozesse senden):

test.sh

#!/bin/bash
OFILE='log'
echo 'ok' > ${OFILE}
echo 'screen pid ' $(screen -list | grep sc_test | cut -f1 -d'.' | sed 's/\W//g') >> ${OFILE}
echo 'test.sh pid ' $$ >> ${OFILE}
kill -SIGSTOP $(screen -list | grep sc_test | cut -f1 -d'.' | sed 's/\W//g')
kill -SIGSTOP $$
echo 'after stop' >> ${OFILE}

Bildschirmbefehl

screen -dmS sc_test ./test.sh

Logdatei

ok
screen pid  4453
test.sh pid  4454

Listenbildschirme

screen -list
There is a screen on:
        4453.sc_test    (11/05/2015 10:45:20 AM)        (Detached)
1 Socket in /var/run/screen/S-root.

Antwort2

Prüfung derQuellcode von GNU screen-4.0.2zeigte, dass screen überprüft, ob seine Kindprozesse gestoppt sind und dann versucht, sie mit SIGCONT fortzusetzen. Hier ist ein relevanter Teil davon screen.c:

1561 if (WIFSTOPPED(wstat))
1562   {   
1563     debug3("Window %d pid %d: WIFSTOPPED (sig %d)\n", p->w_number, p->w_pid, WSTOPSIG(wstat));
....
1578     /* Try to restart process */
1579     Msg(0, "Child has been stopped, restarting.");
1580     if (killpg(p->w_pid, SIGCONT))
1581       kill(p->w_pid, SIGCONT);
1582   }   

Dieses Verhalten lässt sich anscheinend nicht mit einer Option oder über eine Konfigurationsdatei ändern. Die vorgeschlagene Lösung, den Bildschirmprozess zu stoppen, könnte unerwünschte Nebenwirkungen haben. Ein besserer Ansatz besteht darin, den gestoppten Prozess auf dem Bildschirm auszublenden. Für ein Bash-Skript kann dies mit einer Subshell erfolgen:

#!/bin/bash
(   
OFILE='log'
echo 'ok' > ${OFILE}
kill -SIGSTOP ${BASHPID}
echo 'after stop' > ${OFILE}
)   

Bei anderen Shells ist es möglicherweise nicht so einfach, beispielsweise bei tcsh:

#!/bin/tcsh
(\  
set OFILE='log' ;\
echo 'ok' > ${OFILE} ;\
kill -STOP `exec sh -c 'echo ${PPID}'` ;\
echo 'after stop' > ${OFILE} \
)   

Der Hauptunterschied ist die Methode zum Abrufen der PID der Subshell. Weitere InformationenHier.

Alternativ kann das Skript ohne Änderungen mit einemVerpackungSkript:

#!/bin/bash
SCRIPTDIR="$(dirname "$(readlink -f -n "$0")")"
${SCRIPTDIR}/test.sh

Laufen:

screen -d -m ./wrapper.sh

verwandte Informationen