Table of Contents

Procesos en foreground / background y desvincularlos de una terminal (nohup y disown)

Cuando se refiere al uso de procesos en primer o segundo plano, se está hablando de un entorno de ejecución de shell, es decir, procesos que han sido ejecutados en una terminal. No todos los procesos ejecutados en una consola tienen que depender de la misma como proceso hijo, por ejemplo gvim depende del proceso init.

Se explicará cómo manejar procesos en primer y segundo plano, como pasar de un estado a otro y como desvincularlos de una determinada terminal. También se mostrará como identificar los procesos según dichos modos de ejecución (ps / top) e incluso como es posible revincular un determinado proceso a otra shell (cambiar de shell un proceso).

Lectura recomendada: http://www.gnu.org/software/bash/manual/bashref.html#Job-Control-Builtins

Ejecutar un comando en primer plano (sin redirecciones) en una terminal.

Ejecutar un comando en segundo plano (sin redirecciones) en una terminal.

Diferencias entre “&”, “nohup” y “disown”.

El carácter “&” al final de un comando, no tiene el mismo comportamiento que “Cntrl + Z” o “kill -20” o “kill -19”, el “&” no detiene el proceso, simplemente lo deja corriendo en segundo plano. Sería el equivalente a “Cntrl + Z” o “kill -20 XXX” y posteriormente “kill -18 XXX” o “bg X”para reanudarlo. Si la shell es cerrada, el proceso terminará también, al no ser que se ejecute el comando “exit” o “logout” y la opción “huponexit” esté desactivada (comando shopt).

Ejecutar un comando en segundo plano tiene como finalidad principal, el ejecutar un comando cuando el usuario necesita salir, pero cerrando la sesión, en inglés “log out” (pts: comando exit / ttys: comando logout). Si el usuario no hace ese “log out”, si no que cierra la terminal de golpe, el proceso en segundo plano es también eliminado.

Por defecto, en sistemas POSIX, la ejecución de un comando en segundo plano, con “&”, sólo desvincula stdin. Por lo tanto stdout y stderr siguen vinculados al shell padre. Ese es el motivo de que algunos comandos o scripts ejecutados en segundo plano continúen enviando su salida a la terminal donde fueron ejecutados.

NOTA: Cuando hay procesos detenidos, la primera ejecución del comando “exit” nos avisará (Hay trabajos detenidos), si repetimos el comando por segunda vez, se cerrará la terminal y los procesos detenidos morirán también.

No siempre “kill -18” o “bg” pueden reanudar un proceso en background, si el proceso está en segundo plano pero requiere de la entrada estándar no podrá continuar. Veamos un ejemplo.

 (echo 111 ; cat - > /tmp/caca;echo 222)&
[1] 3580
111

[1]+  Detenido                ( echo 111; cat - > /tmp/caca; echo 222 )

jobs -l
[1]+  3580 Parado (requiere entrada de terminal)             ( echo 111; cat - > /tmp/caca; echo 222 )

bg 1
[1]+ ( echo 111; cat - > /tmp/caca; echo 222 ) &

[1]+  Detenido                ( echo 111; cat - > /tmp/caca; echo 222 )

$ fg
( echo 111; cat - > /tmp/caca; echo 222 )
Hola Caracola  # Cntrl + D
222 

NOTA: Para saber el PID del último proceso ejecutado en segundo plano (background) se puede utilizar “$!”

ls -ltd / &
[3] 14302
drwxr-xr-x 17 root root 4096 may 22 23:12 /
[3]-  Hecho                   ls --color=auto -ltd /
 
echo $!
14302

Cuando un proceso se encuentra ejecutándose en segundo plano y finaliza, al pulsar “enter” sobre la consola o ejecutar cualquier orden, esta nos informará. Si la shell no tiene actividad no se mostrará ninguna información, si está ocupada con otro comando, como por ejemplo top, hasta que este no termine no se podrá visualizar la noticia.

[1]+  Hecho                   sleep 6

Mostrar información sobre procesos en segundo plano vinculados a la terminal (ver SID).

ps -j --forest
  PID  PGID   SID TTY          TIME CMD
 3796  3796  3796 pts/6    00:00:00 bash
 4232  4232  3796 pts/6    00:00:00  \_ ejemplo1.sh
 4235  4232  3796 pts/6    00:00:00  |   \_ sleep
 4247  4247  3796 pts/6    00:00:00  \_ ps

El comando “nohup” no envía el proceso a segundo plano al no ser que se especifique “&” al final, nohup simplemente lo separa de la terminal cuando esta se cierra, siendo su nuevo padre el proceso con PID 1 (init). No permite utilizar la entrada estándar aunque el proceso esté en primer plano. Si no se indican redirecciones, la salida estándar y de errores son enviadas a un fichero nohup.out (ver opción “-f”). Se puede jugar con redirecciones para que la salida de errores se muestre por pantalla. Estos son algunos de sus mensajes más típicos dependiendo del tipo de redirección realizado.

nohup date 
nohup: se descarta la entrada y se añade la salida a 'nohup.out'

nohup date > cacas # Que es lo mismo que "date 1> cacas".
nohup: se descarta la entrada y se redirige la salida de error a la salida estándar

###

nohup date 2> cacas 

cat cacas
nohup: se descarta la entrada y se añade la salida a 'nohup.out'

cat nohup.out 
dom abr 14 20:34:50 CEST 2012

NOTA: Si el archivo “nohup.out” no puede ser escrito, el comando no se ejecutará.

disown es muy útil cuando se ha ejecutado un comando en la terminal, pero este no queremos que termine si la shell es cerrada. Esto mismo se puede hacer de otra manera como ya se ha visto en el artículo (& + exit). A diferencia de “nohup”, el comando disown no interfiere con las salidas estándar y de error del comando ejecutado, al igual que nohup tampoco envía el proceso a segundo plano. Puede ser aplicado sobre cualquier comando en segundo plano ejecutado en una terminal.

AVISO: El proceso debe estar ejecutándose, si está detenido y se cierra la shell, el proceso terminará. el comando disown acepta tanto el jobid como el PID.

 # Desvinculación entre el proceso y la terminal.
jobs
[1]+  Detenido                yes > /dev/null
# jobs -l muestra también el PID: [1]+  3362 Parado                  yes > /dev/null
 
 
bg 1
[1]+ yes > /dev/null &
 
disown %1
 
# La desvinculación no funcionará si no se activa el proceso antes de cerrar la shell.
 
yes > /dev/null
^Z
[1]+  Detenido                yes > /dev/null
 
jobs
[1]+  Detenido                yes > /dev/null
 
disown %1
bash: aviso: borrando el trabajo detenido 1 con grupo de proceso 3362
 
# NOTA: Una vez borrado el trabajo, si no se activa de nuevo el proceso (kill -18 XXX) antes de cerrar la shell, este también terminará.
# Si antes de ejecutar el comando "disown" se ejecutara "bg 1", el proceso entraría en ejecución y seguiría ejecutándose al cerrar la terminal tras el comando "disown".

No todos los comandos que son ejecutados en terminal tienen que depender necesariamente de la shell como proceso padre.

# Proceso padre: init (PID 1).
gvim
ps -fea | grep -i gvim
marta      1517     1  0 16:24 ?        00:00:00 gvim
 
# Proceso padre: shell pts/1 (PID 1452).
find / -name "passwords.pdf"
ps -fea | grep -i find
andrea      1557  1452 11 16:27 pts/1    00:00:00 find / -name passwords.pdf

Al ejecutar un comando usando “nohup”, se indica que al terminar su proceso padre (la shell), este pasa a ser init (PID 1). Por lo tanto puede ser usado para desvincular procesos de una shell, permitiendo cerrar la shell o la conexión ssh sin interrumpir la ejecución del proceso.

nohup sublime

Ejecutar un comando directamente en background.

sublime &

Si se está usando como interprete de comandos la bash y esta tiene la opción “huponexit” a “false” (predeterminado), el comando exit en la terminal permite cerrar la shell enviando los procesos en segundo plano que dependen de ella a init.

shopt huponexit
huponexit      	off
 
sublime & exit

Enviar un proceso en foreground (primer plano) a background (el proceso será detenido).

# Opción 1 (Interactiva).
Cntrl + Z
 
# Opción 2 (Enviar señal SIGTSTP al PID del proceso).
kill -20 XXX

Listar procesos en background (Se debe ejecutar en la misma terminal donde se enviaron a segundo plano).

yes > /dev/null
^Z
[1]+  Detenido                yes > /dev/null
 
# El comando jobs muestra una lista de los procesos en segundo plano.
jobs
[1]+  Detenido                yes > /dev/null

Enviar un proceso en segundo plano a primer plano.

fg 1
yes > /dev/null
 
# También se puede usar el nombre del proceso o parte del nombre, por ejemplo:
# fg yes
# fg ?s
# fg ?ull

Cuando se detienen un proceso, este es enviado a background. De la misma manera, cuando un proceso es enviado a segundo plano, este es también detenido. El símbolo “+” en la salida de jobs indica el último proceso enviado a background y es el predeterminado a volver a primer o segundo plano si se usa respectivamente “fg” o “bg” sin parámetros.

jobs
[2]-  Ejecutando              yes > /dev/null &
[3]+  Detenido                sleep 35

fg 2
yes > /dev/null
^Z
[2]+  Detenido                yes > /dev/null

jobs
[2]+  Detenido                yes > /dev/null
[3]-  Detenido                sleep 35

Es posible enviar señales a procesos para que pasen a segundo plano sin utilizar “Cntrl + Z” o para arrancarlos cuando estos se encuentran en segundo plano.

kill -20 PID # (SIGTSTP) Detiene y por lo tanto envía el proceso a segundo plano.
kill -18 PID # (SIGCONT) Reanuda un proceso (no lo envía al primer plano, para ello se 
debe usar fg).
 
# Se pueden enviar señales por medio del jobid "%JOBID"
kill -20 %1 # Mostrar un mensaje: [1]+  Detenido                yes > /dev/null
kill -18 %1 # A diferencia de "bg %1" NO avisa de que se ha reanudado el proceso, pero informaría si no pudiera reanudarlo (ejemplo con cat).

NOTA: Las señales enviadas con kill no surtirán efecto si el proceso ya se ha desvinculado de la shell.

Comando ps: Estados de los procesos en primer / segundo plano.

# T  Proceso parado.
busi      1463  4.2  0.0   5836   688 pts/1    T    16:16   1:29 yes
 
# R En ejecución o en la cola de ejecución dentro del grupo de procesos en primer plano (foreground).
busi      1463  3.7  0.0   5836   688 pts/1    R+   16:16   1:18 yes
 
# R En ejecución o en la cola de ejecución dentro del grupo de procesos en segundo plano (background).
busi      1463  4.5  0.0   5836   688 pts/1    R    16:16   1:38 yes
 
# Al desvincular una shell de un proceso y cerrarse, en el campo tty aparecerá "?".
sandra      3657 99.0  0.0   5836   664 ?        R    22:44   2:23 yes

NOTA: El comando top también muestra el estado en la columna “S”, aunque no permite conocer si el proceso está o no en primer plano.

# PS Process states.
D Uninterruptible sleep (usually IO).
R Running or runnable (on run queue).
S Interruptible sleep (waiting for an event to complete).
T Stopped, either by a job control signal or because it is being traced.
W paging (not valid since the 2.6.xx kernel).
X dead (should never be seen).
Z Defunct ("zombie") process, terminated but not reaped by its parent.

Additional characters.

< high-priority (not nice to other users).
N low-priority (nice to other users).
L has pages locked into memory (for real-time and custom IO).
s is a session leader.
l is multi-threaded (using CLONE_THREAD, like NPTL pthreads do).
+ is in the foreground process group. 

Para desvincular una determinada terminal de alguno de los comandos que está ejecutando, cuando no se tiene acceso físico, se recomienda leer el siguiente artículo: Enviar comandos a una terminal determinada.

Otro comando que puede ser utilizado para enviar a primer o segundo plano procesos, es screen, pero está orientado a su uso con conexiones remotas. el comando “trap” también puede ser utilizado para realizar una tarea similar a nohup, pero no lo trataremos.

Recuperar / redirigir / vincular procesos a una nueva shell/terminal con Reptyr

reptyr es una utilidad para tomar un programa en ejecución existente y conectarlo a una nueva consola. Funciona bien mientras se use siempre root, por lo menos bajo mi experiencia, puede depender del kernel utilizado.

IMPORTANTE: Es posible recuperar procesos en segundo plano siempre que NO se haya cerrado la terminal. La terminal puede cerrarse una vez se haya pasado el proceso a la nueva shell.

Es una utilidad muy útil cuando se ha conectado mediante ssh para ejecutar una tarea, pero no se utilizó el comando screen y el proceso está actualmente en funcionamiento. La solución más simple es conectar de nuevo al host, usar screen y vincular el proceso a esa nueva sesión con screen mediante reptyr. Si la salida del comando no es importante, no necesitamos de interacción o simplemente no se encuentra screen instalado, tal vez valdría con pasar a segundo plano la aplicación y desvincularse, quedando el proceso funcionando por debajo al cerrar la terminal.

cntrl + z  # Paramos el proceso.
bg 1             # Se arranca en segundo plano.
disown %1        # Nos desvinculamos (Si el proceso está parado lo matará)

NOTA: No todos los procesos sobreviven al cerrar la terminal, pese a que se haya desvinculado, un ejemplo puede ser tcpdump cuando no se redirige a un fichero la salida.

Recuperar la salida (stdout / stderr) de un comando que se ejecuta en segundo plano

Si se cerro la terminal donde se puso un programa en segundo plano y además se desvinculó previamente, este quedará en segundo plano funcionando. Reptyr no sirve aquí ya que se cerró la terminal, pero con un depurador se puede obtener la salida del comando que está en segundo plano (XXX es el PID).

strace -pXXX -s9999 -e write

Usando Ggb también puede cambiarse el stdout (1) y stderr (2) de un proceso en ejecución.

# PID: XXXX
gdb -p XXXX
 
# Cambiar la salida estándar al fichero /tmp/stdout.
p (int)dup2((int)open("/tmp/stdout", 1089, 0777), 1)
# Redirige la salida de error a /dev/null.
p (int)dup2((int)open("/dev/null", 1089, 0777), 2)
# Redireccionando con gdb la salida de error a la terminal virtual /dev/pts/3.
p (int)dup2((int)open("/dev/pts/3", 1089, 0777), 2)

Conceptos básicos sobre entrada / salida estándar y redirecciones

entrada estándar (stdin) 	0 	teclado
salida estándar (stdout) 	1 	pantalla
error estándar (stderr) 	2 	pantalla
comando < fichero # Toma la entrada de fichero.
comando > fichero # Envía la salida de comando a fichero; sobrescribe cualquier cosa de fichero.
comando 2> fichero # Envía la salida de error del comando a fichero (el 2 puede ser reemplazado por otro descriptor de fichero)
comando >> fichero # Añade la salida de comando al final de fichero.
comando << etiqueta # Toma la entrada para comando de las siguientes lineas, hasta una línea que tiene sólo etiqueta.
comando 2>&1 # Envía la salida de error a la salida estándar (el 1 y el 2 pueden ser reemplazado por otro descriptor de fichero, p.e. 1>&2).
comando <<< cadena # Se le envía una cadena de texto al comando.Similar a echo "cadena" | comando
 
comando &> fichero # Envía la salida estándar y de error a fichero; equivale a comando > fichero 2>&1
comando >& fichero # Igual que le anterior, envía la salida estándar y de error a fichero; equivale a comando > fichero 2>&1
 
# Redirigir salida estándar y/o salida de errores a la vez que se redirige a un fichero.
comando > fichero 2>&1   # Sobrescribe el fichero.
comando >> fichero 2>&1  # Concatenando al contenido del fichero.
 
# Process Substitution (Bash): "<(comando)" y ">(comando)".
# Leer: http://mywiki.wooledge.org/ProcessSubstitution
tr 'c' 'x'  < <(echo -e "c\nab\na") # Lo mismo que echo -e "c\nab\na" | tr 'c' 'x'
 
# El caso anterior es útil para aplicarlo a bucles while.
while read i; do echo -e "parámetro : $i"; done < <(echo -e "a\nab\nc")
parámetro : a
parámetro : ab
parámetro : c

Desactivar / Reactivar la salida estándar y/o de errores en scripts de forma global

# Redireccionar la salida estándar a /dev/null.
exec 1>/dev/null
# Redireccionar la salida de errores a /dev/null.
exec 1>/dev/null
# Redireccionar la salida estándar y de errores a /dev/null.
exec 1>/dev/null 2>&1 
 
# Restaurar la entrada y salida estándar a su comportamiento normal.
exec >/dev/tty 2>&1

Tuberías y el uso de redirecciones: En algunos casos, el primer comando muestra el resultado deseado como “salida de errores” y las tuberías de manera predeterminada solo redirigen stdin (entrada estándar). Por lo tanto en algunas ocasiones es necesario usar las tuberías de la siguiente manera en combinación con las redirecciones para que todo funcione correctamente.

# Dos maneras de redireccionar la salida de errores a la entrada estándar usando tuberías.
comando 2>&1 | comando2
comando |& comando2

Redirecionar la salida a ficheros encontrados mediante el comando find.

# En todos los ficheros encontrados agregará una linea en blanco y posteriormente una linea conteniendo variable: "valor"
find  . -type f -print0 | xargs -0 -i sh -c ' echo -e "\nvariable: \"valor\"" >> {}'

Conceptos básicos sobre los IDs asociados a los procesos en Linux

Las sesiones y grupos de procesos son sólo maneras de tratar una serie de procesos relacionados como una unidad. Los miembros de un grupo de procesos siempre pertenecen a la misma sesión, pero una sesión pueden tener múltiples grupos de procesos.

Normalmente, una shell es líder de la sesión y cada proceso ejecutado en ella será un grupo de procesos. Se puede ver con el siguiente ejemplo.

ps fo pid,ppid,pgid,sid,comm
  PID  PPID  PGID   SID COMMAND
 5019  5012  5019  5019 bash           # Líder de la sesión (5019).
 5027  5019  5027  5019  \_ man        # Líder del grupo de proceso 5027 .
 5040  5027  5027  5019      \_ man    # Pertenece al grupo de proceso 5027.
 5042  5027  5027  5019      \_ less   # Pertenece al grupo de proceso 5027.
 4493  4486  4493  4493 bash           # Líder de la sesión (4493).
 4911  4493  4911  4493  \_ bash       # Líder de grupo / Hijo del líder de sesión 4493.
 4932  4911  4932  4493      \_ top    # Líder de grupo / Hijo de 4911.
 4995  4911  4995  4493      \_ sleep  # Líder de grupo / Hijo de 4911.
 4999  4911  4999  4493      \_ sleep  # Líder de grupo / Hijo de 4911.
 5048  4911  5048  4493      \_ ps     # Líder de grupo / Hijo de 4911.
 3967  3960  3967  3967 bash
 4838  3967  4838  3967  \_ top
 4841  3967  4841  3967  \_ top
 2830  2823  2830  2830 bash

Señales estándares (NO son las únicas): http://man7.org/linux/man-pages/man7/signal.7.html (man 7 signal)

Señales con múltiples valores indican que el número no es igual dependiendo de la arquitectura en uso. El primero valor es por lo general para arquitecturas alpha y SPARC, el segundo para x86 / ARM / Otras y la última para MIPS.

Signal     Value     Action   Comment
────────────────────────────────────────────────────────────────────────────────────────────────────
SIGHUP        1       Term    Hangup detected on controlling terminal or death of controlling process
SIGINT        2       Term    Interrupt from keyboard
SIGQUIT       3       Core    Quit from keyboard
SIGILL        4       Core    Illegal Instruction
SIGABRT       6       Core    Abort signal from abort(3)
SIGFPE        8       Core    Floating point exception
SIGKILL       9       Term    Kill signal
SIGSEGV      11       Core    Invalid memory reference
SIGPIPE      13       Term    Broken pipe: write to pipe with no readers
SIGALRM      14       Term    Timer signal from alarm(2)
SIGTERM      15       Term    Termination signal
SIGUSR1   30,10,16    Term    User-defined signal 1
SIGUSR2   31,12,17    Term    User-defined signal 2
SIGCHLD   20,17,18    Ign     Child stopped or terminated
SIGCONT   19,18,25    Cont    Continue if stopped
SIGSTOP   17,19,23    Stop    Stop process
SIGTSTP   18,20,24    Stop    Stop typed at terminal (A diferencia de SIGSTOP puede ser ignorada)
SIGTTIN   21,21,26    Stop    Terminal input for background process
SIGTTOU   22,22,27    Stop    Terminal output for background process


Acciones
────────────────────────────────────────────────────────────────────────────────────────────────────

Term   Default action is to terminate the process.
Ign    Default action is to ignore the signal.
Core   Default action is to terminate the process and dump core (see core(5)).
Stop   Default action is to stop the process.
Cont   Default action is to continue the process if it is currently stopped.

Congelar aplicaciones aplicando chekpoints para reanudarlos posteriormente (criu)

CRIU permite crear punto de control / restauración operando siempre desde el espacio de usuario. Es una herramienta de software para sistema GNU/Linux que permite congelar aplicación en ejecución (o parte de ella), con sus ficheros, sockets, etc. Hay que tener en cuenta que criu no puede funciona con todas las aplicaciones del sistema, si no con algunas que cumplan determinados requisitos.

Criu: https://criu.org/Main_Page

Criu Vídeo: https://asciinema.org/a/15653

Escenarios de uso: https://criu.org/Usage_scenarios

# Tips Fuente: https://coelhorjc.wordpress.com/2014/10/27/howto-checkpoint-and-restore-a-linux-process-
using-criu/

# dump, checkpoint a running process; process will terminate and its state will be stored in set of files in current directory
$ criu dump -t <PID>
 
# restore a dumped process to its original running state
$ criu restore -t <PID>
 
# if process launched from shell use '--shell-job'
$ criu dump -t <PID> --shell-job
$ criu restore -t <PID> --shell-job
 
# to re-parent restored process to init, use 'restore-detached'
$ criu restore -t <PID> --shell-job --restore-detached
 
# to checkpoint and restore a process w/ active TCP connections, use '--tcp-established'; uses http://lwn.net/Articles/495304/
$ criu dump -t <PID> --tcp-established
$ criu restore -t <PID> --tcp-established
 
# live migration application or container between machines, see http://criu.org/Live_migration
$ criu dump --tree <pid> --images-dir <path-to-existing-directory> --leave-stopped
$ scp -r <path-to-images-dir> <dst>:/<path-to-images>
$ criu restore --tree <pid> --images-dir <path-to-images>