Jobs y procesos en la shell de GNU/Linux

Publicado por Diego Córdoba el

A raíz de las encuestas semanales que estamos publicando en las redes sociales, surgió una interesante duda sobre procesos y jobs en Linux, diferencias y cómo gestionarlos. Veamos!

Procesos en Linux

Un proceso es un programa en ejecución, así de simple.

Cuando ejecutamos cualquier comando, como ls, el sistema ejecuta el programa asociado, un binario en este caso particular, y crea un proceso que se encarga de listar los archivos del directorio actual. Obviamente, cuando crea el proceso lo ejecuta y finalmente lo termina.

Cada proceso del sistema tiene datos asociados:

  • PID (process ID): identificador numérico del proceso (único en el sistema)
  • PPID (parent PID): PID del proceso padre, el que lo creó, por ejemplo, la shell.
  • Estado: el estado del proceso, si está corriendo, detenido, durmiendo, etc.

Podemos ver los procesos con herramientas como top, htop, ps, pstree, etc.

La shell y la creación de procesos

La shell es el intérprete de comandos, como bash o zsh. Cuando escribimos un comando como ls, la shell crea un proceso hijo como clon suyo, y le da funcionalidad. Esto lo hace con llamadas al sistema como fork o exec.

Luego de crear el proceso, esperará hasta que termine, y nos devolverá el prompt para poder seguir trabajando.

Cuando un proceso está en ejecución en nuestra shell, y nos «roba» el prompt, es decir, no podemos lanzar otro proceso hasta que no termine el anterior, se dice que el proceso que toma control de la shell está ejecutándose en primer plano, o foreground.

Cuando el proceso se lanza y ejecuta, pero nos permite seguir trabajando en la shell, se dice que el proceso se ejecutó en segundo plano, o background.

Luego vamos a ver que, en realidad, estos estados de primero o segundo plano son más propios de un «job» que de un proceso en si.

Señales y control de procesos

Linux permite enviar señales entre procesos, de modo que un proceso que envía una señal puede controlar a otro que la recibe, y cambiar su estado o comportamiento.

Algunas de las principales señales son:

  • SIGTERM (15): pide terminar un proceso de manera limpia y ordenada.
  • SIGKILL (9): termina un proceso de inmediato.
  • SIGSTOP (19): detiene la ejecución de un proceso (se puede reanudar)
  • SIGCONT (18): continúa la ejecución de un proceso detenido.

Las señales pueden enviarse de diferentes formas, siendo las más comunes:

  • El uso del comando kill/killall/pkill: permite enviar señales a un proceso.
  • El uso de combinaciones de teclas: Algunas combinaciones de teclas en la terminal envían señales a los procesos en ejecución. Por ejemplo, Ctrl+z suspende un proceso en ejecución enviando la señal SIGSTOP, mientras que Ctrl+c envía la señal SIGTERM al proceso.

Jobs y procesos dentro de una shell

Hasta aquí hemos hablado de procesos, y cabe mencionar que se trata de entidades globales dentro del sistema operativo. Cualquier proceso del sistema puede ver al resto de los procesos en ejecución.

Pero la shell donde lanzamos nuestros comandos nos permite interactuar con sus procesos de una manera diferente (además, por supuesto, de la manera global).

La shell ve a los procesos locales como «jobs». Un job es un proceso, o grupo de procesos, que la shell gestiona directamente.

Esto le permite pausar, reanudar o mover procesos del primero al segundo plano de manera sencillo.

Así como los procesos tienen su identificador, el PID, los jobs también tienen el suyo, el Job ID.

¿Qué diferencia a un job de un proceso?

Los jobs son procesos que se generan en una shell, pero en el fondo, son procesos como cualquier otro.

Podríamos decir que si un proceso que está en ejecución, y fue lanzado desde una shell interactiva, además de proceso, esa shell lo va a ver, además, como un job.

Los procesos son visibles desde todo el sistema, pero como jobs, solamente son visibles desde la shell que los lanzó.

Ahora bien, si tenemos procesos y herramientas para gestionarlos en el sistema, ¿De qué nos sirven los jobs?

Los jobs tienen la finalidad de facilitarnos algunas tareas de gestión de procesos propios de una shell.

Gestión de jobs con fg, bg y jobs

Cuando ejecutamos un programa o comando cualquiera desde la shell, se crea un proceso hijo, con su PID global, y se asocia con un número de job propio de la shell.

Cuando el proceso terminal, desaparece tanto del ambiente global (tabla de procesos del sistema) como del ambiente local de la shell (job).

Cuando el proceso demora más tiempo en terminar es cuando podemos administrarlo como job y aprovechar las facilidades de la shell.

Veamos esto con un ejemplo.

Si ejecutamos el comando ls /, su ejecución será tan fugaz que esto de crear el proceso hijo, asociarlo a un job local, terminar y desaparecer, será tan rápido que no nos dará tiempo a verlo, sin embargo ocurre igual.

Si ejecutamos un comando que demore más tiempo, como sleep 500, veremos que nos roba el control de la terminal, es decir, se ejecuta en primer plano, la forma predeterminada, y mientras se ejecute, no podremos usar el prompt para ejecutar otro comando.

Podemos matar al proceso y recuperar el prompt de la shell usando, por ejemplo, la combinación Ctrl+c, que enviará una señal SIGTERM al proceso en cuestión:

$ sleep 500
^C
$ jobs
$ ps fax|grep sleep | grep -v grep
$

Sin embargo, si en lugar de usar la combinación Ctrl+c usamos Ctrl+z, en vez de enviar la señal SIGTERM enviaremos SIGSTOP, y el proceso se suspenderá. En este caso podremos verlo con el comando jobs.

En este ejemplo también he ejecutado el comando ps para ver el estado del proceso dentro del sistema. Puede verse que está en estado T, que, como sabemos, significa detenido (Stopped).

$ sleep 500       
^Z
[1]  + 201227 suspended  sleep 500
$ jobs   
[1]  + suspended  sleep 500
$ ps fax|grep sleep | grep -v grep            
 201227 pts/4    T      0:00              |   \_ sleep 500

Como puede verse, el proceso sleep se generó, con el PID 201227 dentro del sistema, y con el ID de job 1 dentro de la shell.

La salida del comando jobs muestra, además del número de job entre corchetes, el PID del proceso.

Si vamos a otra shell y ejecutamos el comando jobs no vamos a ver el job, sin embargo, si ejecutamos ps sí veremos el proceso asociado.

Reanudando el proceso suspendido

Para reanudar el proceso en primer plano solamente debemos ejecutar el comando fg:

$ sleep 500
^Z
[1]  + 201227 suspended  sleep 500
$ fg
[1]  + 201227 continued  sleep 500

Si tuviéramos más de un job en nuestra shell, deberíamos añadir el número de job a reanudar en primer plano, precedido por un símbolo porcentual:

$ fg %1

Esta ejecución trajo nuevamente al proceso sleep al primer plano, y se hizo con el control de la shell, por lo que no podremos utilizarla salvo que matemos al proceso, o lo suspendamos nuevamente, o…

Reanudando el proceso en segundo plano

También podemos reanudar el proceso sin traerlo al primer plano, que salta de su estado detenido y vuelva a la cola de procesos listos para ejecutarse, utilizando el comando bg:

$ sleep 500
^Z
[1]  + 202548 suspended  sleep 500
$ jobs
[1]  + suspended  sleep 500
$ bg   
[1]  + 202548 continued  sleep 500
$ jobs
[1]  + running    sleep 500
$ 

Como podemos ver, el proceso se ha reanudado en segundo plano (continued) y el comando jobs nos lo muestra como «running».

En el caso de que tuviéramos varios jobs y quisiéramos reanudar uno particular, deberíamos especificar el número de jobs al comando bg de esta forma:

$ bg %1

Ejecutando un proceso en segundo plano

También podemos ejecutar un proceso directamente en segundo plano añadiendo el símbolo & al final de la línea de comandos.

Por ejemplo, podemos ejecutar el proceso sleep 500 en segundo plano, y podremos seguir utilizando la shell, por ejemplo, para ver los jobs activos:

$ sleep 500 &              
[1] 205413
$ jobs
[1]  + running    sleep 500
$ 

Al igual que en el caso anterior, también podemos llevarlo al primer plano con fg, suspenderlo en segundo plano con Ctrl+z, y reactivarlo en segundo plano con bg.

¿Y si enviamos señales con kill?

Sí, estas tareas que hemos realizado de manera muy sencilla utilizando combinaciones de teclas, y comandos de gestión de jobs también pueden llevarse a cabo enviando señales con kill, e invocando a los procesos por su PID.

Los comandos bg y fg envían señales a los procesos asociados a los jobs de la shell, igual que las combinaciones de teclas mencionadas:

Y como los procesos son visibles desde todo el sistema operativo, cualquier sesión de la shell podría enviar una señal a cualquier proceso, incluso si es un job de otra sesión.

Veamos un ejemplo… directamente en una animación, luego lo explicamos:

jobs y procesos en linux - ejemplo 1

En este ejemplo se ve cómo se pueden manipular los jobs de una sesión de la shell desde otra terminal, usando el envío de señales del sistema.

En la primer shell se ejecuta un proceso, sleep 500, y en la segunda buscamos su PID con el comando ps, luego enviamos la señal STOP al proceso, lo que se ve reflejado en la primer terminal.

En la primer terminal podemos ver el estado del proceso/job usando el comando jobs.

Podemos reactivar el job en segundo plano enviando, desde cualquier terminal (en este caso la segunda) la señal CONT, y con jobs en la primer terminal podemos ver que efectivamente se volvió a ejecutar, ahora en segundo plano.

Si enviamos la señal TERM, el proceso terminará normalmente, y en el estado en jobs veremos «Terminated».

Veamos un segundo ejemplo:

jobs y procesos en linux - ejemplo 1

En este caso ejecutamos el comando sleep 500 directamente en segundo plano dentro de la primera shell. Esto inmediatamente nos devuelve el prompt para seguir trabajando. Podemos ejecutar el comando jobs para ver las tareas en ejecución.

Ahora, en la segunda terminal enviamos la señal STOP para detenerlo, y verificamos el funcionamiento en la primera con el comando jobs.

Luego lo reanudamos con la señal CONT, y finalmente lo terminamos con KILL.

En este caso lo hice con KILL para que veamos que no es una terminación normal del proceso. Antes, con TERM veíamos en la salida de jobs la leyenda «Terminated», ahora vemos «Killed», lo que indica que le proceso ha sido eliminado forzosamente por el kernel.

Conclusión: jobs y procesos

En fin, un proceso es una entidad global en Linux, identificada por su PID.

Un job, en cambio, es un proceso (o grupo de procesos) gestionado por la shell actual.

Desde cualquier parte del sistema podemos enviarle señales a los procesos con kill, para detenerlos, reanudarlos, terminarlos, matarlos, etc.

Desde la shell que lanzó los procesos podemos gestionarlos, además, con jobs, fg, bg, o añadiendo & a la línea de órdenes. De esta forma podemos ejecutar procesos en primer plano, en segundo plano, detenerlos, reanudarlos, o moverlos entre primero y segundo plano.

Por cualquier ampliación del uso de estos comandos de jobs, podemos consultar sus páginas de manual:

Entender estos conceptos, y las facilidades que nos brindan los jobs nos permiten manejar la shell de forma más flexible y productiva.

Espero que les sirva!

Cualquier duda me escriben!


¿Preguntas? ¿Comentarios?

Si tenés dudas, o querés dejarnos tus comentarios y consultas, sumate al grupo de Telegram de la comunidad JuncoTIC!
¡Te esperamos!

Categorías: Linux

Diego Córdoba

- Ingeniero en Informática - Mgtr. Teleinformática - Instructor GNU/Linux, Programación, Redes TCP/IP, criptografía y ciberseguridad. - (Ex) Docente universitario e investigador