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.
- El proceso hereda stdin stdout stderr de la shell, lo que la hace dependiente de la misma.
- Si la shell es cerrada (señal SIGHUP / hangup), también se envía una señal SIGHUP / hangup al proceso en ejecución.
- Si no se cierra la terminal, los shell espera (está bloquea) hasta que el proceso termina su ejecución.
Ejecutar un comando en segundo plano (sin redirecciones) en una terminal.
- El proceso hereda stdout / stderr de la shell, stdin es bloqueada.
- El proceso es colocado en la lista de trabajos en segundo plano gestionado por la shell.
- Si la shell recibió una señal SIGHUP, también envía un SIGHUP al proceso.
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
- Process ID (PID): Es único, se asigna de forma semialeatoria, una vez finalizado el ID puede ser reasignado.
- Parent Process ID (PPID): El proceso padre es el primer proceso que arranca.
- Process Group ID (PGID): Es el PID del líder del grupo de procesos. Si el PID y PGID son iguales, significa que el proceso es el líder del grupo. Varios procesos dentro de un grupo indican el uso de tuberías.
- Session ID (SID): PID del proceso líder de la sesión. Si el PID y SID son el mismo, indica que el proceso es el líder de la sesión.
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>