Nov 232007
 

La gestión de procesos

Procesos en segundo plano y en primer plano

Cuando os hablé de comandos internos y externos, no sé si recordaréis que introduje el concepto de programa y proceso. Un programa existe sin necesidad de que esté ejecutándose, cuando se ejecuta un programa se inicia un proceso que termina cuando el programa finaliza. Un mismo programa puede estar ejecutándose en varios procesos distintos de forma simultánea. Hasta ahora todas las ordenes que escribíamos aunque fuesen compuestas, ejecutan los programas o los comandos de forma secuencial, es decir no comenzamos otro hasta que finaliza el primero. Esto es así incluso aunque los metamos de una sola vez en una orden compuesta con cualquiera de los separadores vistos hasta ahora. Los procesos en los que se ejecuta cada comando no son pues simultáneos.


Un separador que nos permite ejecutar varios comandos de forma simultánea es el ‘&’ (un solo ampersand, y no dos). Por ejemplo si ponemos:
$ du /bin & ps u & echo esto es una prueba
Se ejecutarán los tres comandos simultáneamente, y obtendremos su salida no necesariamente en el mismo orden que los hemos tecleado. Lo más probable es que el comando echo termine primero, luego termine el ps y finalmente el du, pero eso dependerá mucho de cada máquina. El segundo proceso se lanza inmediatamente después de lanzar el primero pero sin esperar a que finalice, y el tercero se lanza inmediatamente después del segundo, sin esperar la finalización de ninguno de los anteriores, y cuando el tercer proceso ha finalizado (no tienen porque haberlo hecho los anteriores) se devuelve el control al bash que nos presenta el prompt. Así que probablemente si lanzas esa orden, el prompt te quede arriba, justo después de que echo haya escrito ‘esto es una prueba’, y a continuación ps y du lanzan su salida.

Si hubiésemos finalizado la orden con un ‘&amp:’ tendríamos el prompt incluso antes de que la última orden finalizase. Esto puede ser útil cuando queremos ejecutar un comando que tarda mucho y mientras termina queremos seguir trabajando. Por ejemplo si queremos obtener 4000 cifras decimales del número pi podemos usar la calculadora ‘bc’ para hacerlo, tal que así:
bc -l -q < <<"scale=4000; 4*a(1)"
El comando es bc, los parámetros -l y -q son para que use la librería matemática, en la que tiene las funciones trigonométricas que necesitamos y la q es para que no muestre nada más que el resultado. Lo que queremos calcular se lo introducimos, como si lo hubiésemos tecleado, mediante la expresión <<<”scale=4000; 4*a(1)”. Fijamos la precisión (scale) a 4000 cifras decimales y calculamos el producto de 4 por el arcotangente de 1 (Pi/4=Arcotangente(1)).
Pero esto puede tardar en calcularse varios segundos o varios minutos (depende de la potencia de cálculo de tu ordenador), así que lo más practico es terminar la orden con un & y seguir trabajando hasta que termine ¿no?

bc -l -q < <<"scale=4000; 4*a(1)" &

A los procesos que se lanzan y se dejan trabajando, sin esperar a que terminen se les denomina procesos en segundo plano. Un proceso puede estar detenido, ejecutándose en segundo plano o ejecutándose en primer plano (en primer plano solo puede haber un proceso, detenidos o en segundo plano todos los que quieras. Bueno, todos no, el administrador puede, mejor dicho: debe, establecer cuotas para que el usuario no pueda abrir todos los procesos que quiera).

Supongamos que tecleamos la orden anterior, la que calculaba PI y la hemos lanzado en primer plano, (sin ponerle el & al final). Mientras se está ejecutando nos damos cuenta de que teníamos algo más importante que hacer y no podemos esperar a que la orden finalice. Siempre podemos pulsar <control>Z, con lo que la orden que está en primer plano se detiene, y la shell nos devuelve el prompt para que podamos hacer eso que tanta prisa nos corría. La orden no ha finalizado, está parada. Si ejecutamos el comando jobs nos muestra todos los procesos que nuestra shell tiene abiertos y su estado, que en este caso es ‘stopped’ es decir parado.
$ jobs
[1]- Stopped bc -l -q < <<"scale=4000; 4*a(1)" &

Cuando queramos podemos hacer que continúe, y podemos hacerlo continuar de dos formas, en primer plano o en segundo plano, el comando para pasar un proceso al primer plano es fg (de foreground=primer plano) y el comando para reanudar en segundo plano es bg (de backgrund=al fondo). A esos comandos normalmente hay que especificarle el número de proceso (ese numerito que sale entre corchetes cuando pedimos un listado de los procesos con el comando jobs) precedido de un signo ‘%’ que indica proceso, tal que así:

$ bg %1
$ fg %1

Otra cosa que podemos hacer con un proceso es matarlo, con lo cual deja de ocupar recursos y finaliza, y ya no se puede ‘resucitar’. Para matar un proceso se usa el comando kill, al que también se le pasa como argumento el número de proceso precedido del símbolo ‘%’.

$ kill %1

El comando kill en realidad lo que hace es mandar una señal al proceso. La señal por defecto es la de terminación ‘SIGTERM’, pero pueden enviarse otras, como por ejemplo ‘SIGKILL’ que es mucho más dura. SIGTERM normalmente se usa para finalizar el proceso de forma razonable, por ejemplo si hay datos sin guardar se guardarían antes de finalizar le proceso, pero con SIGKILL no, se mata el proceso inmeditamante sin guardar nada. Para ver la lista de posibles señales usa el comando ‘kill -l’ o consulta ‘man 7 signal’ (no pongas man signal sin más porque en ese caso te llevará a la sintáxis de la función del lenguaje C que maneja las señales). Entendido el asunto de las señales veamos como podemos parar o reanudar un proceso que se encuentra en segundo plano. El kill de bash es bastante flexible y permite poner la señal mediante un nombre o por su número precedida de ‘-s ‘ o simplemente de un signo menos para diferenciarla del número de proceso’. Incluso si usamos nombres podemos omitir el prefijo (SIG) por el que empiezan todos los nombres de señal. Así que cualquiera de estas formas es equivalente, yo que soy muy vago me quedo con una de las dos últimas, preferiblemente la última, porque me resulta más sencillo recordar los nombres que los números.
$ kill -s SIGSTOP %1
$ kill -SIGSTOP %1
$ kill -s 19 %1
$ kill -19 %1
$ kill -STOP %1

Evidentemente podemos reanudar un proceso parado con cualquiera de estas formas:

$ kill -s SIGCONT %1
$ kill -SIGCONT %1
$ kill -s 18 %1
$ kill -18 %1
$ kill -CONT %1

Procesos y Trabajos

Como sabemos, Linux es Multiusuario y Multitarea. Hasta ahora hemos visto como en una shell se pueden ejecutar varios procesos simultáneamente, y la orden jobs, nos muestra todos los procesos abiertos desde nuestra shell, numerados. Hay que saber que, al mismo tiempo, seguro que hay muchos más procesos corriendo en Linux, que no fueron iniciados por nosotros desde nuestra shell, pueden ser procesos que el sistema arranca automáticamente o procesos que arrancan otros usuarios desde otras consolas, cada proceso tiene un número único a nivel del sistema llamado PID (Proccess IDent = Identificador de proceso), no hay que confundir este número con el número de ‘job’ que la shell le da a nuestros procesos. Cuando lanzamos un proceso en segundo plano o detenemos un proceso, la shell nos indica dos números, el primero entre corchetes ‘[]‘ es el numero de proceso para la shell y el segundo es el número de proceso en el sistema, el PID. Para no liarnos con los dos números, de aquí en adelante, al número de proceso que asigna la shell le llamaremos ‘trabajo’, reservando la palabra ‘proceso’ para referirnos a la numeración que asigna el sistema.

bc -l -q < <<"scale=4000; 4*a(1)" &
[1] 28777

Aquí podemos ver que nuestro cálculo de PI es el trabajo 1 y el proceso 28777 (en mi caso, vosotros evidentemente obtendréis números distintos). Otros usuarios pueden tener también su trabajo 1, pero el proceso 28777 es único en el sistema, ningún otro proceso puede tener el mismo número. Si consultamos con el comando jobs solo se muestra el número de trabajo.

$ jobs
[1]+ Running bc -l -q < <<"scale=4000; 4*a(1)" &

El comando ps nos muestra los procesos del sistema. Si no le decimos otra cosa nos mostrará los que son de nuestro usuario y nuestra terminal.

$ ps
PID TTY TIME CMD
19399 pts/2 00:00:00 bash
28777 pts/2 00:00:01 bc
28796 pts/2 00:00:00 ps

El comando kill puede usarse con el número de trabajo o con el número de proceso indistintamente, en mi caso concreto las dos órdenes siguientes serían equivalentes.

$ kill %1
$ kill 28777

No sé si os habéis fijado que al lado de los números de trabajo, a veces aparece un signo ‘+’ o un signo ‘-’. Estos signos tienen su significado.
Introduzcamos por ejemplo las siguientes ordenes que calculan el número PI, el número e, y el logaritmo natural de 2, todos ellos con 4000 cifras significativas para que se tomen algo de tiempo, (si tu máquina es muy rápida puedes calcular más cifras).

$ bc -l -q < <<"scale=4000; 4*a(1)" &
[1] 28987
$ bc -l -q < <<"scale=4000; e(1)" &
[2] 28989
$ bc -l -q < <<"scale=4000; l(2)" &
[3] 29026
$ jobs
[1] Running bc -l -q < <<"scale=4000; 4*a(1)" &
[2]- Running bc -l -q < <<"scale=4000; e(1)" &
[3]+ Running bc -l -q < <<"scale=4000; l(2)" &

El trabajo actual (el último que se ha iniciado) se marca con un signo ‘+’, el anterior con un signo ‘-’. El resto de los trabajos no tienen denominación especial. Decíamos que con las ordenes kill, fg y bg siempre había que especificar el número de trabajo (%1, %2, %3…) al trabajo actual nos podemos referir con el identificador especial ‘%%’ o con ‘%+’ y al trabajo anterior con ‘%-’. Así que en nuestro caso concreto las siguientes ordenes son equivalentes.
$ kill %3
$ kill %%
$ kill %+

Y estas también:
$ kill %2
$ kill %-

Kill se puede usar para matar trabajos o procesos, así que siempre hay que poner los números de trabajo con % y los números de proceso sin él. En cambio fg y bg, solo se pueden usar con números de trabajos, así que si omitimos el signo de %, funcionan igual. Es lo mismo poner ‘fg %1′ que ‘fg 1′, o ‘bg %1′ que ‘bg 1′. Pero no es lo mismo ‘kill %1′ que ‘kill 1′ ¡eh!.

En fg y en bg si no se pone número de proceso se entiende que nos referimos al proceso actual, estas tres órdenes son equivalentes:

$ bg %+
$ bg %%
$ bg

Y Para rematar, el comando fg se puede omitir siempre que el número de trabajo lo pongamos precedido de %, así que si (como yo) eres vago escribiendo puedes poner solamente ‘%2′ cuando quieras poner ‘fg %2′.

nice y renice

Aunque no es algo específico de bash, en linux, al ser un sistema multitarea existen utilidades para administrar la prioridad de cada proceso. Habrá procesos que consumen muchos recursos y otros menos. Uno de los recursos más preciados es uso de la cpu. Todos los procesos se están ejecutando al mismo tiempo en una misma cpu (bueno ahora con procesadores de doble y cuádruple nucleo, pueden repartirse en varias), en realidad lo que hace la cpu es repartir su tiempo en pequeños intervalos dedicando unos ciclos de reloj a cada proceso. Hay procesos que la mayoría del tiempo no están haciendo nada, porque deben esperar por un dato del usuario, o de un dispositivo más lento. Esos procesos consumen poca cpu, en cambio otros que hacen cálculos intensivos, consumen más tiempo. El sistema operativo usa un planificador que asigna unas prioridades a cada proceso, y lo hace de forma dinámica, variando esta prioridad para cada proceso según los recursos que va utilizando, de acuerdo a unas reglas que intentan mantener la máxima disponibilidad de recursos. Los procesos con mayor prioridad dispondrán de más tiempo de cpu. Además de esto el usuario puede asignar un nivel de prioridad (NICE) a cada proceso cuando lo lanza. Disponemos de 40 niveles NICE desde -20 (prioridad más alta) hasta 19 (prioridad más baja) lo normal es que los procesos se arranquen con nivel 0. Pero si arrancamos un proceso con un nivel distinto sus hijos heredaran este nivel. El root puede asignar valores negativos a los procesos que arranca, pero los usuarios solo positivos, quiere esto decir que como usuarios solo podemos arrancar un proceso con menos prioridad de la habitual. En cualquier caso es útil: imagínate que queremos lanzar un programa de esos que consumen bastante cpu, pero no tenemos prisa porque acabe y queremos que el sistema no se ralentice mucho porque queremos seguir haciendo otras cosas. Podemos lanzar el proceso en segundo plano pero con una prioridad menor (un valor mayor de NICE) a la normal. Para hacer esto usaremeos el comando nice:
nice -n 19 bc -l -q < <<"scale=6000; 4*a(1)" &

(El comando anterior arrancará la calculadora bc en segundo plano para calcular las primeras 6000 cifras decimales del número PI), pero lo hará con una prioridad muy pequeña.

Podemos ver que procesos consumen más recursos con el comando top, que nos sacará ordenados de mayor a menor los comandos que más uso de cpu consumen. También muestra otra información general, como la cantidad de usuarios que están en el sistema, el número total de procesos y de estos cuantos están activos, cuantos durmiendo, cuantos detenidos y cuantos zombies; la memoria total, la usada y la libre, etc…

El listado que nos mostrará contendrá el número de PID, el usuario que lo está ejecutando, la prioridad del proceso (PRI), el valor nice (NI), el tamaño del proceso (SIZE), el tamaño total del proceso junto con los datos que maneja (RSS), el tamaño usado por el proceso en la memoria (SHARE), el estado del proceso(STAT), el tamaño de las librerías del proceso (LIB), el porcentaje de CPU ( %CPU) y de memoria (%MEM) así como también el tiempo de ejecución (TIME) y el nombre del proceso (COMMAND).

Para salir de top pulsaremos la tecla ‘q’

Si queremos mostrar solo los procesos que más consumen de un determinado usuario podemos pulsar la ‘u’ y se nos pedirá el nombre del usuario (dejarlo en blanco para mostrar todos).

Desde top también podemos interactuar (si tenemos permiso para ello) matando un proceso o modificando su prioridad (nice). Usaremos la tecla ‘k’ para matar un proceso y la ‘r’ para cambiarle la prioridad.

Ya vimos como lanzar un proceso con menos prioridad con nice, pero ¿que ocurre si ya hemos lanzado un proceso y decidimos luego cambiarle la prioridad?

Podemos usar el comando renice seguido de un valor de nice y del numero de proceso. Pero hay que tener en cuenta que un usuario jamás puede aumentar la prioridad de un proceso (dar valores más bajos de nice), solo disminuirla, y que no puede modificar la prioridad a procesos que no sean suyos. Sólo el root puede aumentar o disminuir la prioridad según quiera.

Parámetros y variables relativas a procesos

Hay ciertos parámetros y variables interesantes que podemos consultar que nos dán información sobre los procesos:

$$ Es el numero de PID del bash que estamos ejecutando.

Esta variable puede sernos muy útil en scripts, por ejemplo para crear ficheros temporales únicos, ya que no puede haber dos procesos con el mismo pid.

$! Es el número de PID de la órden más reciente ejecutada en segundo plano.

Nos permitirá conocer fácilmente el numero de pid de un proceso en background, por si hay que matarlo o mandarle alguna señal.

$SHLVL Cada vez que arranquemos una subshell este número se incrementa en 1. Hay que tener en cuenta que siempre que invocamos a un script se ejecuta una subshell.

$PPID El proceso padre del nuestro.

wait

Este comando en principio no hace nada más que esperar a que un determinado ejecutado en segundo plano termine. Si ponemos un número de proceso (PID) o un identificador de trabajo (prefijado con % igual que ya vimos antes) se esperará por ese proceso. Si no ponemos nada wait espera a que terminen todos los proceso hijos que haya en segundo plano. Esto es útil en scrips si lanzamos ciertos procesos en segundo plano, pero es necesario que alguno termine antes de acometer la siguiente tarea.