¿Se puede congelar un proceso temporalmente en Linux?

53

Me preguntaba si hay una manera de congelar algún proceso durante un cierto período de tiempo.

Lo que quiero decir es que: ¿es posible que una aplicación (probablemente ejecutándose como root) pause la ejecución de otro proceso que ya se está ejecutando (cualquier proceso, tanto GUI como línea de comando) y luego lo reanude más tarde? En otras palabras, no quiero que el planificador de Linux programe ciertos procesos durante un cierto período de tiempo.

Pal Szasz
fuente

Respuestas:

81

Ver aquí

Hay dos señales que pueden suspender la ejecución de un proceso. Uno es "agraciado" y el otro es "contundente".

El "elegante" es SIGTSTP, y su propósito es "amablemente" pedirle al proceso, si lo desea, suspender la ejecución hasta que reciba un SIGCONT. En el caso de SIGTSTP, el proceso puede ignorar SIGTSTP y continuar ejecutándose de todos modos, por lo que esto requiere la cooperación de un programa diseñado para manejar SIGTSTP.

El "contundente" es SIGSTOP, y su propósito es suspender todos los hilos del espacio de usuario asociados con ese proceso. Es tan imposible ignorar el proceso SIGSTOPcomo ignorarlo SIGKILL(este último mata el proceso con fuerza).

Para enviar una señal arbitraria, incluyendo cualquiera de los mencionados aquí, se puede utilizar programas como kill, killallo pkill; o use la llamada del sistema kill(2). Consulte las páginas de manual de su sistema operativo para obtener detalles de la plataforma / arquitectura / versión dependiente y erratas con respecto a cualquiera de los anteriores. Tenga en cuenta que la palabra "kill" en todos estos comandos y syscall es un nombre inapropiado. Estos comandos no están diseñados, exclusivamente, para terminar procesos. Ellos pueden hacerlo mediante el envío de ciertos de las señales; pero las señales también se pueden usar para una funcionalidad que no sea terminar un proceso. Por ejemplo, SIGSTOPsolo suspende el proceso, y es solo una de varias señales que se pueden enviar de esta manera.

Para agregar una condición de reanudar automáticamente el proceso después de que haya transcurrido un período de tiempo, deberá usar algún tipo de proceso de monitoreo que permanezca en ejecución y establecer un temporizador para activar el proceso de monitoreo, que a su vez llama kill(2)nuevamente y envía la SIGCONTseñal al proceso detenido, para solicitar que el núcleo reanude la ejecución. Tenga en cuenta que Linux tiene varios mecanismos de temporización con diversos grados de precisión y precisión; Además, si su sistema está muy ocupado, es posible que su proceso de monitoreo no se despierte hasta mucho después de que su temporizador haya expirado, por lo que la activación podría retrasarse.

Si depende de la precisión muy exacta de la suspensión y reanudación del proceso de suspensión, es posible que tenga que ejecutar el programa de monitoreo con permisos en tiempo real (ver esta página de manual de sched_setscheduler(2)la información acerca de hacer su proceso en tiempo real). También puede usar temporizadores de alta resolución, una característica del kernel de Linux (que solo está disponible si su hardware les brinda soporte), en combinación con la programación en tiempo real, para obtener una precisión muy precisa y submilisegunda en el tiempo, luego despertar y enviar la señal para reanudar el proceso monitoreado muy rápidamente.

No indicó qué tecnologías está dispuesto a utilizar para implementar esto. Como mínimo, necesitará al menos secuencias de comandos bash, aunque no podrá obtener una sincronización muy fina de esa manera. Aquí hay un "script" bash (no probado, así que tenga cuidado) que es solo una prueba de concepto de su consulta. Si necesita una sincronización precisa, tendrá que escribir un programa, probablemente en C / C ++ u otro idioma nativo, y utilizar la programación en tiempo real y hrtimers.

#!/bin/bash
#This is the process you want to suspend.
screen -mdS child bash -c "cat /dev/urandom | base64"
#This is the process ID of the child process
THEPID=$(screen -list | grep child | cut -f1 -d'.' | sed 's/\W//g')
#Send SIGSTOP to the child process.
kill -SIGSTOP ${THEPID}
#Now it is suspended. This process will sleep for 10 seconds asynchronously, then resume the process.
screen -mdS monitor bash -c "sleep 10; kill -SIGCONT ${THEPID}"

Tenga en cuenta que la secuencia de comandos finalizará y la secuencia de comandos de control finalizará, pero debido a que screencontrola el proceso del monitor, continuará ejecutándose en segundo plano durante 10 segundos (según el argumento pasado sleep) y luego se activará y continuará el proceso secundario. Pero esto pasará mucho tiempo después de que el script de control haya finalizado. Si desea esperar sincrónicamente el tiempo transcurrido, simplemente omita la segunda llamada screeny codifique el modo de suspensión y elimínelo en el script de control.

Puede probar que el proceso de hecho se suspende ejecutando

screen -rS child

después de comenzar este script. No verá nada en la consola. Luego, después de que el temporizador caduque (10 segundos), inundará su pantalla con datos de base64 (caracteres aleatorios de 0-9 y AF). Presione Ctrl + C para salir.

allquixotic
fuente
Muy buena respuesta muy completa, incluido el fragmento de PoC bash. ¡Agradable!
fgysin
Si un proceso se congela en el medio de una sesión de red como una conexión TCP, cuando se continúa, ¿podría esa sesión de red dañarse o caerse? ¿Hay un comportamiento definido para esto?
CMCDragonkai
@cmcdragonkai porque como el proceso detenido no se ejecuta, no puede leer ni escribir en ningún fd, pero el núcleo sigue transfiriendo bytes hasta que su búfer esté lleno en una sesión TCP, es decir, no hay transferencia entre el espacio del núcleo y el espacio del usuario. los procesos de congelación son diferentes ver https://www.kernel.org/doc/Documentation/power/freezing-of-tasks.txt
Nizam Mohamed
He descubierto que las conexiones TCP pueden continuar después de volver a poner el proceso en primer plano (los tiempos de espera son específicos de la aplicación, hay TCP keepalive pero también de nivel de aplicación keepalive). Los procesos de congelación parecen significar cuando la computadora está en hibernación o en modo de suspensión. ¿Está bien?
CMCDragonkai
Este script no tiene efecto en códigos no cooperativos: gist.github.com/ceremcem/864dd94b52ffe7b18da29558df4f1f5b
ceremcem
14

Sí, puede hacerlo enviando una señal de PARADA a un proceso para suspenderlo y luego un CONT para continuar.

uso:

kill -STOP <pid>

kill -CONT <pid>

fuente
11

Si tiene una definición adecuada de "congelar", puede consultar el renicecomando.

Renice le permite modificar la prioridad de programación de los procesos en ejecución.

El valor normal del proceso normal es 0. Aumentar el valor agradable hace que el proceso sea más agradable, como en "por qué no vas primero". Si bien disminuir el valor agradable hace que un proceso sea menos agradable, como en "sal de mi camino, tengo prisa". El rango de valores agradable es de -20 a 19.

Cualquiera puede hacer que sus propios procesos sean más agradables. Solo root puede hacer que un proceso sea menos agradable o cambiar la simplicidad de los procesos de otros usuarios.

Si establece un valor agradable de procesos en 19, solo se ejecutará cuando nada más en el sistema lo desee.

Aquí hay un ejemplo que se ejecuta en mi caja de Linux local.

Use ps -ly mire la columna NI para ver un buen valor de procesos.

-> ps -l
FS UID PID PPID C PRI NI ADDR SZ WCHAN TTY CMD
0 S 29190402 31146 0 75 0 - 16547 espera pts / 0 bash
0 T 29190 1105402 0 75 0 - 23980 acabado pts / 0 vim
0 R 29190 794402 0 76 0 - 15874 - pts / 0 ps

La ejecución renice +10en el proceso vim hace que se ejecute con una prioridad más baja.

-> renice +10 -p 1105
1105: antigua prioridad 0, nueva prioridad 10

-> ps -l
FS UID PID PPID C PRI NI ADDR SZ WCHAN TTY CMD
0 S 29190402 31146 0 76 0 - 16547 espera pts / 0 bash
0 T 29190 1105402 0 95 10 - 23980 acabado pts / 0 vim
0 R 29190 1998402 0 78 0 - 15874 - pts / 0 ps

Suponiendo que puede estirar "congelar" para que signifique "no molestar a nadie más en el sistema", podría escribir algo así como:

renice 20 -p <pid de interés>
dormir <cierta cantidad de tiempo>
renice 0 -p <pid de interés>

(recuerde ejecutarlo como root).


tenga en cuenta que me tomé algunas libertades con el ps -lresultado anterior para que las columnas interesantes se muestren bien en los pequeños cuadros azules :)

Maurice
fuente