¿Por qué SIGSTOP no funciona dentro de la sesión de pantalla?

¿Por qué SIGSTOP no funciona dentro de la sesión de pantalla?

Considere el siguiente guión:

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

En un shell interactivo, el script se detiene y el resultado es ok. Sin embargo, si se inicia como

screen -d -m ./test.sh

la salida es after stop. ¿La pantalla maneja la señal? ¿Cómo suspender un proceso en una sesión de pantalla?

Respuesta1

Probablemente el proceso de pantalla esté reiniciando el proceso bash detenido. Intenté lo siguiente (enviando señal SIGSTOP a ambos procesos):

prueba.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}

comando de pantalla

screen -dmS sc_test ./test.sh

archivo de registro

ok
screen pid  4453
test.sh pid  4454

pantallas de listado

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.

Respuesta2

examen de lacódigo fuente de GNU screen-4.0.2mostró que la pantalla verifica si sus procesos secundarios están detenidos y luego intenta reanudarlos con SIGCONT. He aquí una parte relevante de 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   }   

Parece que este comportamiento no se puede cambiar con una opción o mediante un archivo de configuración. La solución sugerida para detener el proceso de detección puede tener efectos secundarios indeseables. Un mejor enfoque es ocultar el proceso detenido de la pantalla. Para un script bash, esto se puede hacer con un subshell:

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

Para otros shells puede que no sea tan sencillo, por ejemplo, para tcsh:

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

La diferencia clave es el método para obtener el PID del subnivel, más informaciónaquí.

Alternativamente, el script se puede iniciar sin modificaciones con unenvolturaguion:

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

Correr:

screen -d -m ./wrapper.sh

información relacionada