← Terminal y Línea de Comandos en Linux Bloque 7 · Procesos y entorno · Tema 33 de 43

Tema 33

Trabajos y señales

Una copia de varios gigabytes ocupa la terminal durante veinte minutos, y mientras tanto no se puede escribir nada. Un programa dejó de responder y la ventana no cierra. Una tarea larga, lanzada en un servidor remoto, se interrumpe en cuanto se corta la conexión. En el tema anterior aprendimos a mirar los procesos; en este aprendemos a gobernarlos: mandarlos a trabajar al fondo, pausarlos, traerlos de vuelta y terminarlos. Todo se hace con un único mecanismo, tan antiguo como Unix: las señales.

1. Primer plano y segundo plano

Desde el primer tema, cada comando siguió el mismo ciclo: se escribe, se pulsa Intro, la shell lo ejecuta y espera a que termine antes de mostrar otra vez el indicador. Con ls la espera no se nota. Con un comando que tarda, la terminal queda tomada. Ese modo de ejecución tiene nombre, y tiene una alternativa.

Un comando en primer plano (foreground) ocupa la terminal: recibe lo que se teclea, y la shell espera a que termine. Un comando en segundo plano (background) se ejecuta sin ocupar la terminal: la shell muestra el indicador enseguida y se puede seguir trabajando.

Primer planoSegundo plano
La shellEspera a que termineDevuelve el indicador de inmediato
El tecladoLo recibe el comandoLo recibe la shell
Ctrl+C y Ctrl+ZLo afectanNo le llegan
Cuántos puede haberUno por terminalLos que se quiera
Sirve paraLo breve y lo interactivoLo que tarda y no necesita atención

En el tema anterior resolvimos el problema abriendo una segunda terminal para el sleep. Es una solución legítima, y a veces la mejor. Pero no siempre hay otra ventana a mano —en una conexión remota, en una consola sin escritorio—, y la shell tiene sus propios recursos para manejar varios programas desde un solo lugar. Se los conoce como control de trabajos.

Usaremos sleep como programa de prueba: es inofensivo, no escribe nada y dura lo que se le pida. Todo lo que hagamos con él vale para una copia, una descarga o una compilación.

2. &: lanzar en segundo plano

Para ejecutar un comando en segundo plano se agrega un signo & al final de la línea:

terminal, en ~/practica
$ cd ~/practica
$ sleep 3000 &
[1] 7702
$ █

El indicador volvió al instante, y el sleep está en marcha. La shell respondió con dos números, que identifican lo mismo de dos maneras:

la respuesta de la shell, leída
[1] 7702
 │   │
 │   └─ el PID: el número del proceso para todo el sistema
 └─ el número de trabajo: el que le da esta shell

El proceso existe y se ve con las herramientas del tema 32, conectado a la misma terminal que la shell, que es su padre:

terminal
$ ps
    PID TTY          TIME CMD
   3187 pts/0    00:00:00 bash
   7702 pts/0    00:00:00 sleep
   7704 pts/0    00:00:00 ps
$ echo $!                            # el PID del último comando lanzado en segundo plano
7702

El signo & es un terminador de comando, como el punto y coma del tema 25, con otra consigna: «ejecuta esto y no esperes». No hay que confundirlo con &&, que encadena dos comandos con una condición, ni con el & de 2>&1, que pertenece a las redirecciones.

Cuando un trabajo en segundo plano termina, la shell lo avisa. No interrumpe lo que se esté escribiendo: espera al siguiente indicador, y entonces muestra una línea. Se ve con un segundo trabajo, breve:

terminal
$ sleep 5 &
[2] 7705
$                                    # pasados cinco segundos, pulsamos Intro
[2]+  Done                    sleep 5

El primer trabajo, de cincuenta minutos, sigue en marcha. Elegimos duraciones largas para que no se nos terminen a mitad del tema.

3. jobs: los trabajos de la shell

Un trabajo (job) es un comando, o una tubería entera, que la shell lanzó y tiene bajo su control. Cada shell numera sus trabajos por separado, a partir de 1.

Lanzamos otro trabajo largo y pedimos la lista con jobs:

terminal
$ sleep 4000 &
[2] 7707
$ jobs
[1]-  Running                 sleep 3000 &
[2]+  Running                 sleep 4000 &
$ jobs -l                            # con el PID
[1]-  7702 Running                 sleep 3000 &
[2]+  7707 Running                sleep 4000 &
ParteSignifica
[2]El número de trabajo.
+El trabajo actual: el último que se lanzó o se suspendió. Sobre él actúan fg y bg cuando no se les dice otra cosa.
-El trabajo anterior al actual.
Running, Stopped, DoneEl estado: en ejecución, suspendido, terminado.
sleep 4000 &La línea de comandos.

Trabajo y proceso no son la misma cosa, aunque aquí coincidan, y la diferencia explica por qué hay dos numeraciones:

ProcesoTrabajo
Lo administraEl kernelLa shell
Se identifica conEl PID: 7702El número de trabajo: %1
El número valeEn todo el sistemaSolo en la shell que lo lanzó
Se lista conpsjobs
ContieneUn programa en ejecuciónUno o varios procesos: una tubería completa es un solo trabajo

La última fila es la razón de ser del concepto. sort archivo | uniq -c | less son tres procesos, y para quien lo escribió es una sola tarea: la shell los agrupa en un trabajo, y los suspende, reanuda o termina juntos. La tercera fila tiene una consecuencia práctica: jobs en otra terminal no muestra nada, porque esa es otra shell, con su propia lista.

Para referirse a un trabajo se usa el signo %:

Se escribeDesigna
%1, %2El trabajo con ese número.
%+ o %%El trabajo actual, el marcado con +.
%-El anterior, el marcado con -.
%sleepEl trabajo cuyo comando empieza con ese texto.

4. Ctrl+Z, bg y fg

El signo & sirve cuando se sabe de antemano que el comando va a tardar. Lo habitual es darse cuenta después: el comando ya está en marcha, en primer plano, y la terminal está tomada. Para ese caso hay tres herramientas que trabajan juntas.

HerramientaEfecto
Ctrl+ZSuspende el comando que está en primer plano: lo deja en pausa, y devuelve el indicador.
bgReanuda un trabajo suspendido, en segundo plano (background).
fgTrae un trabajo al primer plano (foreground), esté suspendido o en ejecución.

Las probamos con los dos trabajos que tenemos. Primero traemos uno al frente:

terminal
$ fg %1
sleep 3000
█                                    # la terminal queda ocupada; pulsamos Ctrl+Z
^Z
[1]+  Stopped                 sleep 3000
$ jobs
[1]+  Stopped                 sleep 3000
[2]-  Running                 sleep 4000 &

fg escribió el comando, para recordar de qué se trata, y la terminal quedó como si lo hubiéramos lanzado de la manera común. Con Ctrl+Z, que la terminal representa como ^Z, el trabajo quedó Stopped. El término engaña: no terminó. Está congelado, con todo su estado en memoria, sin avanzar. Ahora lo reanudamos, pero al fondo:

terminal
$ bg %1
[1]+ sleep 3000 &
$ jobs
[1]+  Running                 sleep 3000 &
[2]-  Running                 sleep 4000 &

La secuencia Ctrl+Z y luego bg es la respuesta a «lancé algo largo y me olvidé del &»: el resultado es idéntico a haberlo puesto. Como el trabajo recién suspendido es el actual, bg sin argumentos alcanza.

Los tres estados de un trabajo y cómo se pasa de uno a otro: un comando común empieza en primer plano; con Ctrl+Z pasa a suspendido; con bg pasa de suspendido a segundo plano; con fg vuelve al primer plano, tanto desde suspendido como desde segundo plano; un comando terminado en ampersand empieza directamente en segundo plano comando comando & Primer plano ocupa la terminal Suspendido Stopped: en pausa Segundo plano Running: trabaja Ctrl+Z bg fg fg
Los tres estados de un trabajo. Ctrl+Z solo actúa sobre el que está en primer plano; fg trae a cualquiera de vuelta.

Con el trabajo otra vez al frente, lo terminamos con Ctrl+C, y queda uno solo:

terminal
$ fg                                 # sin argumento: el trabajo actual
sleep 3000
^C
$ jobs
[2]+  Running                 sleep 4000 &

Aquí está, por fin, la explicación que quedó pendiente en el tema 13. Pulsar Ctrl+Z dentro de vim, de less o de man no los cierra: los suspende, y aparece el indicador con un Stopped encima. El archivo sigue abierto, con los cambios sin guardar. Con fg se vuelve exactamente al lugar donde se estaba. Usado a propósito es un recurso cómodo: suspender el editor, ejecutar dos comandos en la shell y regresar.

Un trabajo suspendido no se ve, y sigue ahí

El descuido clásico es suspender un editor con Ctrl+Z, olvidarlo, y abrir el mismo archivo otra vez. Quedan dos editores sobre un mismo archivo, uno de ellos invisible. Si al escribir exit la shell responde There are stopped jobs, es eso: hay trabajos en pausa. jobs los muestra, y fg permite volver a cada uno para cerrarlo como corresponde.

5. Lo que un trabajo escribe y lee

Enviar un comando al segundo plano le quita el teclado, y nada más. Sus tres canales estándar, heredados de la shell, siguen apuntando a la terminal. Si el comando escribe algo, lo escribe ahí, en medio de lo que se esté haciendo:

terminal
$ sleep 5 && echo "Listo el respaldo" &
[3] 7750
$ Listo el respaldo
█                                    # el cursor quedó sin indicador; pulsamos Intro
[3]+  Done                    sleep 5 && echo "Listo el respaldo"
$ 

El mensaje apareció cinco segundos después, a continuación del indicador que ya estaba dibujado. No se rompió nada: la shell sigue esperando un comando, y basta pulsar Intro para tener un indicador limpio. Con un comando que escribe cientos de líneas, en cambio, la terminal se vuelve inusable. La regla es redirigir:

terminal: un trabajo en segundo plano, bien lanzado
$ find / -name "*.conf" > /tmp/conf.txt 2> /dev/null &
[3] 7760
$ wc -l /tmp/conf.txt                # se puede mirar el resultado mientras se va formando
214 /tmp/conf.txt
$ 
[3]+  Exit 1                  find / -name "*.conf" > /tmp/conf.txt 2> /dev/null

La salida fue a un archivo, los errores a /dev/null, y el & va al final de todo, después de las redirecciones. El aviso de cierre dice Exit 1 en lugar de Done: el comando terminó con código de salida 1, porque find tropezó con directorios que no pudo leer. Done es código cero.

Con la entrada ocurre lo contrario. Un trabajo en segundo plano no puede leer del teclado, que pertenece a quien está en primer plano. Si lo intenta, el kernel lo suspende, y la shell lo informa como Stopped (tty input). Hay que traerlo con fg para responderle. Por eso los programas interactivos —un editor, un comando que pide confirmación— no se lanzan con &.

Un trabajo en segundo plano…Qué ocurreQué hacer
Escribe en su salida o en la de errores.El texto aparece en la terminal, mezclado.Redirigir: > archivo 2>&1 &
Intenta leer del teclado.Queda suspendido.No lanzarlo así; o fg.
Termina.La shell lo avisa antes del siguiente indicador.Nada.

6. Las señales

Ctrl+C termina un programa; Ctrl+Z lo suspende; bg lo reanuda. Parecen tres mecanismos distintos, y son uno solo.

Una señal es un aviso que se le envía a un proceso para informarle de un acontecimiento o pedirle algo. No lleva datos: es un número, de un repertorio fijo. El proceso lo recibe en cualquier momento, interrumpiendo lo que estuviera haciendo.

Cada señal tiene un número y un nombre que empieza con SIG. Hay unas sesenta; en el uso diario importan ocho:

SeñalN.ºPide o avisaLa envíaEfecto habitual
SIGHUP1«Se cerró tu terminal.»El cierre de la ventana o de la conexión.Termina.
SIGINT2«Interrumpe lo que haces.»Ctrl+CTermina.
SIGQUIT3«Abandona, y deja un registro.»Ctrl+\Termina.
SIGKILL9Nada: el kernel lo elimina.kill -9Termina, sin excepción.
SIGTERM15«Termina, por favor.»kill, y el apagado del sistema.Termina.
SIGCONT18«Continúa.»fg, bgReanuda si estaba suspendido.
SIGSTOP19Nada: el kernel lo congela.kill -STOPSe suspende, sin excepción.
SIGTSTP20«Suspéndete.»Ctrl+ZSe suspende.

La columna de la derecha dice «habitual» por una razón que es el centro del tema. Al recibir una señal, un proceso puede hacer una de tres cosas:

El proceso puede…Qué significaEjemplo
Dejar la acción por defecto.Lo que el kernel hace si el programa no previó nada: casi siempre, terminarlo.sleep ante Ctrl+C.
Atender la señal.El programa tiene preparada una respuesta propia: guardar, cerrar archivos, despedirse.Un editor que guarda una copia de emergencia antes de salir.
Ignorarla.El programa decidió no hacer caso.nano y less ante Ctrl+C.

De ahí el vocabulario cortés de la tabla: una señal es un pedido, y el programa resuelve cómo responder. Hay dos excepciones, las marcadas «sin excepción»: SIGKILL y SIGSTOP no llegan nunca al programa. Las ejecuta el kernel directamente, y no se pueden atender ni ignorar.

Las combinaciones de teclas no tienen nada de mágico: la terminal las convierte en señales para el trabajo que está en primer plano. Eso explica por qué Ctrl+C no afecta a los trabajos del fondo.

TeclaEnvíaPara
Ctrl+CSIGINTInterrumpir.
Ctrl+ZSIGTSTPSuspender.
Ctrl+\SIGQUITTerminar cuando Ctrl+C no alcanza.
Ctrl+DNingunaNo es una señal: marca el fin de la entrada, como vimos en el tema 22.

La lista completa de señales la da kill -l:

terminal
$ kill -l | head -n 4
 1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
 6) SIGABRT	 7) SIGBUS	 8) SIGFPE	 9) SIGKILL	10) SIGUSR1
11) SIGSEGV	12) SIGUSR2	13) SIGPIPE	14) SIGALRM	15) SIGTERM
16) SIGSTKFLT	17) SIGCHLD	18) SIGCONT	19) SIGSTOP	20) SIGTSTP

Algunos nombres son viejos conocidos con otra cara. SIGSEGV es el célebre Segmentation fault: la señal que el kernel le envía a un programa que intentó usar memoria que no le pertenece. Y SIGPIPE es la que recibe un comando cuando escribe en una tubería que ya nadie lee: así se detiene sort en sort archivo | head -n 1, cuando head ya tuvo su línea y terminó.

7. kill: enviar una señal

El teclado solo alcanza al trabajo que está en primer plano. Para enviarle una señal a cualquier otro proceso está el comando kill.

kill envía una señal a uno o más procesos, identificados por su PID o por su número de trabajo. Si no se indica cuál, envía SIGTERM.

El nombre es desafortunado: kill no mata, envía señales, y varias de ellas no terminan nada. Empezamos por el uso común, con el trabajo que quedó:

terminal
$ kill %2                            # por número de trabajo
$ 
[2]+  Terminated              sleep 4000
$ sleep 1000 &
[1] 7771
$ kill 7771                          # por PID
$ 
[1]+  Terminated              sleep 1000

La señal se indica con un guion, y se puede escribir de tres maneras equivalentes: por número, por nombre, o por nombre completo.

ComandoEnvíaPara
kill PIDSIGTERMPedirle que termine. La forma normal.
kill -15 PID, kill -TERM PID, kill -SIGTERM PIDSIGTERMLo mismo, dicho expresamente.
kill -INT PIDSIGINTEl equivalente de Ctrl+C, a distancia.
kill -HUP PIDSIGHUPEn muchos servicios, releer la configuración.
kill -STOP PIDSIGSTOPCongelarlo.
kill -CONT PIDSIGCONTReanudarlo.
kill -9 PID, kill -KILL PIDSIGKILLEliminarlo sin consultarle. El último recurso.

Con STOP y CONT se puede pausar cualquier proceso propio, aunque no sea un trabajo de esta shell ni tenga terminal. Lo comprobamos con la columna de estado del tema 32:

terminal
$ sleep 1000 &
[1] 7780
$ kill -STOP 7780
$ ps -o pid,stat,cmd -p 7780
    PID STAT CMD
   7780 T    sleep 1000
$ kill -CONT 7780
$ ps -o pid,stat,cmd -p 7780
    PID STAT CMD
   7780 S    sleep 1000
$ kill -9 7780
$ 
[1]+  Killed                  sleep 1000

El estado pasó de S a T, detenido, y de vuelta a S. Es un recurso útil con un programa que está acaparando el procesador o el disco en mal momento: se lo congela, se hace lo urgente, y se lo reanuda, sin perder su trabajo. El aviso final dice Killed y no Terminated: la shell informa con qué señal murió cada trabajo.

Esa información queda también en el código de salida. Un proceso terminado por una señal deja como código 128 más el número de la señal:

Código en $?CuentaEl proceso terminó por
130128 + 2SIGINT: se pulsó Ctrl+C.
137128 + 9SIGKILL.
143128 + 15SIGTERM.

Hay un límite, y es el de siempre: solo se pueden enviar señales a los procesos propios. Los de otro usuario, y los del sistema, requieren sudo.

terminal
$ kill 1                             # el proceso 1 es de root
bash: kill: (1) - Operation not permitted
$ kill 99999
bash: kill: (99999) - No such process

Los mensajes empiezan con bash: porque kill es un comando interno de la shell. Tiene que serlo: solo ella sabe qué proceso es %2.

8. Primero TERM, después KILL

En foros y tutoriales abunda una receta: «para cerrar un programa, kill -9». Funciona siempre, y por eso mismo es una mala costumbre. La diferencia entre las dos señales es la que hay entre pedirle a alguien que se retire y desconectarlo de la corriente.

SIGTERM (15)SIGKILL (9)
Llega al programaSíNo: actúa el kernel
El programa puedeGuardar, cerrar archivos, borrar temporales, avisar a sus hijosNada: desaparece en el acto
Puede ser ignoradaSíNo
RiesgoNingunoArchivos a medio escribir, datos sin guardar, bloqueos y temporales abandonados, procesos hijos huérfanos

Un programa terminado con SIGKILL no tiene oportunidad de ordenar nada. Una base de datos puede quedar con una operación por la mitad; un editor, con el archivo sin guardar; un gestor de paquetes, con un archivo de bloqueo que impedirá ejecutarlo de nuevo. El procedimiento correcto es escalonado:

  1. Pedir: kill PID, que envía SIGTERM. pedir
  2. Esperar unos segundos y comprobar con ps -p PID si sigue ahí. Cerrar ordenadamente puede llevar un momento. esperar
  3. Forzar, solo si no respondió: kill -9 PID. forzar
kill -9 es el último recurso, no el primero

La gran mayoría de los programas termina correctamente con un kill común. kill -9 se reserva para el que está realmente colgado: no responde a SIGTERM porque quedó en un bucle o porque decidió ignorarla. Es el mismo orden que sigue el sistema al apagarse: envía SIGTERM a todos los procesos, les da un tiempo, y recién entonces SIGKILL a los que quedan.

9. pkill y killall: por nombre

kill necesita un PID, y averiguarlo es un paso previo. Dos comandos lo evitan, buscando los procesos por su nombre.

pkill es el hermano de pgrep que anunciamos en el tema anterior: selecciona los procesos de la misma manera, con las mismas opciones, y en lugar de listarlos les envía una señal. Esa simetría da un método seguro: primero pgrep, después pkill.

terminal
$ sleep 600 & sleep 700 & sleep 800 &
[1] 7801
[2] 7802
[3] 7803
$ pgrep -a sleep                     # mirar a quiénes alcanzaría
7801 sleep 600
7802 sleep 700
7803 sleep 800
$ pkill -f "sleep 700"               # solo el que tiene esa línea de comandos
[2]-  Terminated              sleep 700
$ pkill sleep                        # todos los que se llamen sleep
[1]-  Terminated              sleep 600
[3]+  Terminated              sleep 800
ComandoEnvía la señal a
pkill nombreLos procesos cuyo nombre contiene ese texto.
pkill -x nombreLos que se llaman exactamente así.
pkill -f textoLos que tienen ese texto en su línea de comandos.
pkill -u usuarioTodos los procesos de un usuario.
pkill -t pts/1Los conectados a esa terminal.
pkill -STOP nombre, pkill -9 nombreLos mismos, con otra señal.

killall hace algo parecido, con una diferencia de criterio: exige el nombre exacto del programa, no una parte.

pkillkillall
El nombreEs una expresión regular, y coincide con una parteDebe ser exacto
Opciones propias-f, -u, -t, -n, -o-i, pregunta antes de cada uno; -u
Si no encuentra ningunoNo dice nada; código de salida 1Lo informa: no process found
Por nombre, la señal va a todos los que coincidan

pkill sh no termina «la shell»: alcanza a bash, a sshd, a gnome-shell y a todo proceso propio cuyo nombre contenga «sh», empezando quizá por la sesión desde la que se lo escribió. Con sudo delante, el alcance es el sistema entero. Antes de un pkill, el mismo patrón con pgrep -a. Y una advertencia para quien administre otros Unix: en algunos, como Solaris, killall sin más termina todos los procesos.

Para las aplicaciones gráficas hay un recurso más: xkill convierte el puntero en una cruz, y la ventana que se pulse se cierra. Y los monitores del tema 32 traen la función incorporada: en top la tecla k pide un PID y una señal, y en htop F9 ofrece la lista de señales para el proceso seleccionado.

10. Cerrar la terminal: SIGHUP, nohup y disown

Un trabajo en segundo plano no ocupa la terminal, pero sigue perteneciendo a ella. Al cerrar la ventana, o al cortarse una conexión remota, el kernel envía SIGHUP a la shell —el nombre viene de hang up, colgar el teléfono, de cuando las terminales se conectaban por línea telefónica—, y la shell la reenvía a sus trabajos. Todos terminan.

Casi siempre es lo deseable: nadie quiere procesos sueltos de sesiones cerradas. Para la tarea larga que debe sobrevivir a la sesión hay dos recursos, según el momento en que se lo recuerde.

nohup comando & ejecuta el comando de modo que ignore la señal SIGHUP. Si la salida iba a la terminal, la desvía a un archivo llamado nohup.out.

terminal
$ nohup sleep 2000 &
[1] 7820
nohup: ignoring input and appending output to 'nohup.out'
$ nohup sleep 2000 > /tmp/tarea.log 2>&1 &     # mejor: eligiendo el archivo
[2] 7823

nohup es un comando que lanza otro comando, como sudo. No lo manda al segundo plano: el & hay que ponerlo igual. Se puede cerrar la ventana, abrir otra, y el proceso sigue ahí:

en una terminal nueva, después de cerrar la anterior
$ jobs                               # nada: esta es otra shell
$ ps -o pid,ppid,tty,cmd -p $(pgrep -f "sleep 2000")
    PID    PPID TT       CMD
   7820    2731 ?        sleep 2000
   7823    2731 ?        sleep 2000

Tres cosas cambiaron respecto del tema 32. El proceso ya no es un trabajo de ninguna shell, y solo se lo alcanza por su PID. Su terminal es ?: la que tenía ya no existe. Y su padre no es una shell: al morir la que lo lanzó quedó huérfano, y lo adoptó un proceso superior. Es el caso que habíamos anunciado.

El segundo recurso es para cuando el trabajo ya está en marcha, sin nohup, y hay que irse:

RecursoSe usaEfecto
nohup comando &Al lanzar el comando.El comando ignora SIGHUP; su salida va a un archivo.
disown %1Después, con el trabajo ya en segundo plano.La shell lo borra de su lista de trabajos, y no le reenviará SIGHUP.
disown -h %1Lo mismo.Lo protege de SIGHUP pero lo deja en la lista.

disown no redirige nada: si el trabajo escribe en la terminal y esta se cierra, fallará al escribir. Por eso nohup, con las redirecciones puestas, es el más confiable de los dos.

Ambos son soluciones de emergencia. Para trabajar en serio en una máquina remota se usa un multiplexor de terminal, como tmux o screen: mantienen la sesión entera viva en el servidor, con sus programas y su pantalla, y permiten desconectarse y volver a entrar horas después al mismo lugar. Y para un programa que debe funcionar siempre, la herramienta son los servicios del tema 39.

11. nice y renice: ceder el paso

Hay una intervención más suave que pausar o terminar: dejar que el proceso siga, pero sin estorbar. Cuando varios procesos quieren el procesador a la vez, el kernel lo reparte según la prioridad de cada uno, y esa prioridad se puede ajustar con un número llamado nice, la columna NI de top.

El valor nice de un proceso va de -20, la mayor prioridad, a 19, la menor. El valor normal es 0. Cuanto más alto, más «amable» es el proceso: más cede el procesador a los demás.

terminal
$ nice -n 10 sleep 900 &             # lanzar con prioridad rebajada
[1] 7840
$ ps -o pid,ni,cmd -p 7840
    PID  NI CMD
   7840  10 sleep 900
$ renice -n 15 -p 7840               # cambiarla a un proceso en marcha
7840 (process ID) old priority 10, new priority 15
$ renice -n 5 -p 7840                # volver a subirla: no se permite
renice: failed to set priority for 7840 (process ID): Permission denied

El último intento muestra la regla: un usuario común solo puede bajar la prioridad de sus procesos, es decir, aumentar su valor nice. Subirla, o usar valores negativos, es privilegio de root. De otro modo, todos se darían prioridad máxima.

El valor nice no limita nada: un proceso con nice 19, solo en la máquina, usa el procesador entero. Actúa únicamente cuando hay competencia. Por eso es el ajuste indicado para una compresión o una conversión de video larga: nice -n 19 tar -czf respaldo.tar.gz ~/Documentos & trabaja a pleno mientras la máquina está libre, y se aparta en cuanto se la usa para otra cosa.

12. timeout y wait

Dos comandos pequeños completan el repertorio. Los dos tienen que ver con el tiempo: uno pone un límite, el otro espera.

timeout ejecuta un comando y, si no terminó al cabo del tiempo indicado, le envía SIGTERM. Es la protección contra el comando que podría quedarse colgado para siempre:

terminal
$ timeout 3 sleep 100; echo $?       # a los tres segundos lo termina
124
$ timeout 3 sleep 1; echo $?         # terminó a tiempo: su propio código
0

El código de salida 124 significa «se agotó el tiempo». La duración admite sufijos, 30s, 5m, 2h; y con -k 10 se le agrega el segundo escalón del procedimiento de la sección 8: si diez segundos después de SIGTERM el comando sigue vivo, SIGKILL.

wait hace lo inverso de &: detiene la shell hasta que terminen los trabajos en segundo plano. Sin argumentos los espera a todos; con un PID o un %n, a ese. Permite lanzar varias tareas a la vez y continuar recién cuando todas estén listas:

terminal: tres tareas en paralelo
$ sleep 3 & sleep 3 & sleep 3 & wait; echo "Las tres terminaron"
[1] 7851
[2] 7852
[3] 7853
[1]   Done                    sleep 3
[2]-  Done                    sleep 3
[3]+  Done                    sleep 3
Las tres terminaron

El mensaje aparece a los tres segundos, no a los nueve: las tres esperas corrieron al mismo tiempo. Con tres compresiones o tres descargas en lugar de tres sleep, es la manera más simple de aprovechar varios núcleos desde la línea de comandos.

13. Cuando un proceso no termina

A veces se envía la señal y el proceso sigue en la lista. Antes de insistir conviene mirar su estado con ps -o pid,ppid,stat,cmd -p PID, porque cada caso tiene otra explicación:

Se observaQué ocurreQué hacer
No responde a Ctrl+C.El programa atiende o ignora SIGINT.Su orden de salida propia (q, :q, Ctrl+X); o Ctrl+Z y luego kill %1.
No responde a kill.Ignora SIGTERM, o está colgado.Esperar unos segundos; después, kill -9.
Está en estado T y no termina con kill.Está suspendido: la señal queda pendiente hasta que se reanude.kill -CONT PID, o fg.
Está en estado Z, <defunct>.Es un zombie: ya terminó. No hay nada que matar.Nada; o terminar a su padre, que es quien debe recogerlo.
Está en estado D y no responde a kill -9.Espera una operación del disco o de la red que no se puede interrumpir.Esperar: termina cuando la operación concluye o falla.
Termina, y reaparece con otro PID.Otro proceso lo vigila y lo relanza: es un servicio.Detenerlo con systemctl, tema 39.
Operation not permittedNo es un proceso propio.sudo kill PID, si corresponde.

La primera fila tiene un recurso que conviene recordar. Un programa puede ignorar Ctrl+C, pero rara vez ignora Ctrl+Z. Suspenderlo devuelve el indicador, y desde ahí kill %1 lo termina: es la salida de emergencia de casi cualquier programa en el que uno haya quedado atrapado.

La penúltima fila es la más desconcertante la primera vez. Los servicios del sistema están supervisados por systemd, que los reinicia si mueren. Enviarles señales es pelear contra el supervisor; lo correcto es pedirle a él que los detenga.

14. Recetas y errores comunes

Quiero…Comando
Lanzar algo largo sin ocupar la terminalcomando > salida.log 2>&1 &
Mandar al fondo algo que ya está en marchaCtrl+Z, y luego bg
Ver qué tengo en marcha en esta shelljobs -l
Volver a un programa suspendidofg, o fg %n
Interrumpir el comando en primer planoCtrl+C
Terminar un trabajo en segundo planokill %n
Terminar un proceso del que conozco el PIDkill PID
Terminar un programa por su nombrepgrep -a nombre; luego pkill nombre
Forzar a uno que no respondekill -9 PID
Pausar un proceso, y reanudarlo despuéskill -STOP PID; kill -CONT PID
Que una tarea siga al cerrar la terminalnohup comando > salida.log 2>&1 &
Lo mismo, con el trabajo ya lanzadodisown %n
Que una tarea pesada no molestenice -n 19 comando &
Limitar cuánto puede durar un comandotimeout 60 comando
Seguir el avance de una tarea del fondotail -f salida.log
SíntomaCausaSolución
Aparece texto en medio de lo que escribo.Un trabajo en segundo plano escribe en la terminal.Pulsar Intro; la próxima vez, redirigir su salida.
Stopped al lanzar algo con &.El comando quiere leer del teclado.fg; no lanzar con & programas interactivos.
Pulsé Ctrl+Z y el editor «desapareció».Está suspendido, no cerrado.fg.
There are stopped jobs al salir.Quedan trabajos suspendidos.jobs, y cerrarlos con fg.
fg: no such job, aunque el proceso existe.Se lanzó desde otra shell: no es un trabajo de esta.Actuar por PID, con kill.
kill: %1: no such jobLo mismo; o el trabajo ya terminó.jobs para ver cuáles hay.
kill 7702 «no hizo nada».kill no escribe cuando la señal se envió bien.Comprobar con ps -p 7702.
La tarea murió al cerrar la ventana.Recibió SIGHUP.nohup, disown o tmux.
El & quedó en medio y parte de la línea no se ejecutó como se esperaba.& termina el comando: lo que sigue es otro.El & va al final, después de las redirecciones.
pkill terminó programas de más.El patrón coincidía con parte de otros nombres.pgrep -a antes; pkill -x.
Hay un archivo nohup.out que no creé.Lo creó nohup, con la salida del comando.Redirigir la salida a un archivo elegido.

La referencia del tema:

Se escribeQué hace
comando &Ejecuta en segundo plano.
jobs, jobs -lLista los trabajos de esta shell; con sus PID.
Ctrl+ZSuspende el trabajo en primer plano.
bg, bg %nReanuda un trabajo en segundo plano.
fg, fg %nTrae un trabajo al primer plano.
$!El PID del último trabajo lanzado en segundo plano.
Ctrl+C, Ctrl+\Envían SIGINT y SIGQUIT al trabajo en primer plano.
kill PID, kill %nEnvía SIGTERM.
kill -9, -STOP, -CONT, -HUPEnvía otra señal.
kill -lLista las señales.
pkill nombre, killall nombreEnvía una señal por nombre de programa.
nohup comando &Lo protege del cierre de la terminal.
disown %nLo quita de la lista de trabajos de la shell.
nice -n N comando, renice -n N -p PIDLanza con prioridad ajustada; la cambia.
timeout T comandoLo termina si excede el tiempo.
waitEspera a los trabajos en segundo plano.

Antes de seguir conviene dejar todo limpio: pkill -u $USER sleep termina los sleep que hayan quedado, incluidos los de nohup, y rm nohup.out borra el archivo que este creó en el directorio de práctica. Al adoptar un proceso huérfano dijimos que había heredado todo de su padre; entre lo heredado hay algo que todavía no miramos de cerca. Cada proceso recibe al nacer un conjunto de variables que condicionan su comportamiento: dónde buscar los programas, en qué idioma hablar, qué editor abrir. Es el entorno, y es el tema 34.

15. Resumen

16. Preguntas de repaso

Intenta responder cada una antes de abrir la respuesta.

1. ¿Qué diferencia hay entre ejecutar un comando en primer plano y en segundo plano?

En primer plano, la shell espera a que termine y el comando recibe el teclado. En segundo plano, la shell devuelve el indicador de inmediato y el comando se ejecuta sin ocupar la terminal.

2. Al ejecutar sleep 100 & la shell responde [1] 8120. ¿Qué son los dos números?

El 1 es el número de trabajo, que le asigna esa shell. El 8120 es el PID, que identifica al proceso en todo el sistema.

3. ¿Qué diferencia hay entre un trabajo y un proceso?

El proceso lo administra el kernel y se identifica con un PID válido en todo el sistema. El trabajo lo administra la shell, vale solo en ella, y puede agrupar varios procesos, como los de una tubería.

4. Lanzaste un comando largo y olvidaste el &. ¿Cómo lo pasas al segundo plano sin perder lo hecho?

Con Ctrl+Z, que lo suspende, y luego bg, que lo reanuda en segundo plano.

5. Un trabajo figura como Stopped. ¿Terminó?

No. Está suspendido, en pausa, con su estado intacto. Se lo reanuda con fg o bg.

6. Pulsaste Ctrl+Z en vim y apareció el indicador. ¿Qué pasó, y cómo vuelves?

El editor quedó suspendido como un trabajo de la shell, con el archivo abierto. Se vuelve con fg.

7. ¿Por qué conviene redirigir la salida de un comando que se lanza en segundo plano?

Porque sigue conectada a la terminal, y lo que escriba aparecerá mezclado con lo que se esté haciendo.

8. ¿Qué es una señal, y qué puede hacer un proceso al recibirla?

Es un aviso numerado que se envía a un proceso. El proceso puede atenderla con una respuesta propia, ignorarla, o dejar que se aplique la acción por defecto, que suele ser terminar.

9. ¿Qué señales envían Ctrl+C y Ctrl+Z, y a quién?

Ctrl+C envía SIGINT y Ctrl+Z envía SIGTSTP, las dos al trabajo que está en primer plano en esa terminal.

10. ¿Qué señal envía kill 4321?

SIGTERM, la número 15: un pedido de terminar.

11. ¿Por qué no conviene usar kill -9 como primera opción?

Porque SIGKILL elimina el proceso sin que este pueda guardar datos, cerrar archivos ni limpiar temporales. Primero se envía SIGTERM, que le permite terminar ordenadamente.

12. ¿Qué dos señales no puede ignorar ni atender un proceso?

SIGKILL y SIGSTOP: las ejecuta el kernel directamente.

13. ¿Qué precaución hay que tomar antes de un pkill?

Ejecutar el mismo patrón con pgrep -a, para ver exactamente a qué procesos alcanzaría, ya que el patrón coincide con cualquier parte del nombre.

14. ¿Por qué un trabajo en segundo plano termina al cerrar la terminal, y cómo se evita?

Porque recibe la señal SIGHUP. Se evita lanzándolo con nohup comando &, o aplicándole disown si ya está en marcha.

15. Un comando terminó con código de salida 137. ¿Qué le ocurrió?

Fue terminado por la señal 9, SIGKILL: 137 es 128 más 9.

Para practicar

  1. Lanza tres sleep de distinta duración en segundo plano. Míralos con jobs -l y con ps. Abre otra terminal y ejecuta allí jobs y pgrep -a sleep. Explica la diferencia.
  2. Ejecuta sleep 200 en primer plano. Suspéndelo, mira su estado con jobs y con ps -o pid,stat,cmd, reanúdalo en segundo plano, tráelo al frente e interrúmpelo. Comprueba el código de salida con echo $?.
  3. Abre tareas.txt con nano o vim, suspende el editor, ejecuta ls y jobs, y vuelve al editor con fg. Luego intenta salir de la shell con el editor suspendido y lee el mensaje.
  4. Ejecuta ls -R /usr & y observa qué ocurre con la terminal. Repítelo redirigiendo la salida a /tmp/usr.txt, y sigue su avance con wc -l.
  5. Ejecuta cat &. ¿En qué estado queda, y por qué? Tráelo al primer plano, escribe una línea y termínalo con Ctrl+D.
  6. Lanza sleep 500 & y envíale, en orden, STOP, CONT y TERM, mirando la columna STAT de ps después de cada una. Repite terminándolo con INT y con KILL, y compara los avisos de la shell.
  7. Lanza cuatro sleep con duraciones distintas. Con pgrep -a y pkill -f, termina solo uno; luego, con un solo comando, los demás.
  8. Lanza nohup sleep 1000 &, cierra la ventana, abre otra y encuéntralo. Averigua su PPID y su terminal, y termínalo. Repite la experiencia sin nohup.
  9. Lanza nice -n 15 sleep 3000 & y verifica su valor con ps -o pid,ni,cmd. Intenta llevarlo a 19 y luego a 0 con renice. Localízalo en top o htop y míralo en la columna NI.
  10. Prueba timeout 2 sleep 10; echo $? y timeout -s KILL 2 sleep 10; echo $?. Explica los dos códigos. Luego mide con time cuánto tarda sleep 2 & sleep 2 & wait.