Hoy toca el turno de hablar sobre procesos en Unix. Un tema super interesante si te apasiona todo lo que relacionado con administrar servidores o te encuentras en el área de DevOps.
Para este post estaré trabajando con el sistema operativo Ubuntu, sin embargo toda la explicación aquí mencionada, así como los comandos ejecutados, son aplicables para cualquier sistema operativo basado un Unix, llámese Ubuntu, Fedora, Red Hat, MacOs, Debian etc.. 🤓
Así que, sin más introducción, comencemos con esta entrega.
procesos 🚀
Comencemos con la pregunta obligada ¿Qué es un proceso? Verás, en términos simples un proceso no es más que un entorno especial creado para la ejecución de un programa.
Por ejemplo, cuando ejecutamos un programa (ya sea con doble clic o directamente en terminal) un nuevo proceso es creado en nuestro sistema, y dentro de él (dentro del proceso) se encontrará todo lo necesario para que el programa funcione correctamente, me refiero a variables, rutinas, threads, funciones, ciclos etc... todo lo que los desarolladores hayan definido al momento de crear dicho programa. 😎
Por ejemplo, cuando ejecutamos el comando ls, para listar todo el contenido de un directorio , estaremos creando un nuevo proceso, ya que estaremos ejecutando un programa.
Una vez el programa finaliza el proceso lo hará de igual manera. 😃
Con esto en mente, podemos concluir que un proceso es la instancia de un programa. Y como instancia, podemos crear la n cantidad que deseemos. Podemos confirmar esto abriendo un par de veces nuestro navegador. Una nueva ventan, un nuevo proceso.🤠
Ahora, todos los procesos poseen 4 atributos que nos permitirán gestionarlos. Me refiero a:
PID (Process ID): Identificador único para cada uno de los procesos. Este identificador se conforma de hasta 5 números enteros. Si bien el ID de un proceso puede repetir en un cierto periodo de tiempo (Ya que los procesos finalizan y el sistema eventualmente re utilizará algún ID), nunca pueden existir 2 procesos con el mismo ID en el mismo periodo de tiempo.
PPID (Parent Process ID): En Unix, cada proceso es creado, asu vez, por otro proceso, por lo tanto se necesita una referencia al proceso padre. Si lo vemos desde una perspectiva de base de datos, podemos decir que una proceso padre puede poseer múltiples procesos hijos y un proceso hijo pertenece a un proceso padre.
TTY: Terminal a la cual se encuentra asociada un proceso. No todos los procesos pueden ser creados desde una terminal, por lo tanto podrán existir procesos sin este atributo.
UID (User ID): Usuario al que pertenece el proceso. Usuario propietario del proceso.
Para listar todos los procesos activos en nuestro sistema, ejecutaremos el comando ps.
$ ps
PID TTY TIME CMD
21918 pts/0 00:00:00 bash
22166 pts/0 00:00:00 ps
En mi caso encontramos 2 procesos: El proceso Bash, que sería la terminal, donde me encuentro ejecutando los comandos, y ps, el proceso que me permitió listar los procesos.
Si queremos conocer el PPID de cada proceso usamos la bandera -f.
ps -f
UID PID PPID C STIME TTY TIME CMD
eduardo 260 259 0 20:44 pts/0 00:00:00 /bin/bash --login
eduardo 531 260 0 20:49 pts/0 00:00:00 nano
eduardo 534 260 0 21:00 pts/0 00:00:00 ps -f
Antes de continuar quisiera hacer un pequeño paréntesis, ya que quizás te estés preguntando ¿si todos los procesos son creados por otros procesos, esto quiere decir que existe un proceso principal? ¿Un padre de padres? Y la respuesta es sí. Y el proceso tiene por nombre Init process.
Este es el primer proceso que se ejecuta cuando el sistema inicia y posee por PID 1. A partir de él todos los demás procesos podrán crearse.
Si hacemos un árbol genealógico de todos los procesos en el sistema, sin duda terminaremos llegando al Init process. 🌳
Bien, continuemos con los comandos.
Si queremos conocer más información acerca de los procesos, un parámetro muy popular es aux.
$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.5 0.0 1208 564 ? Sl 12:20 1:21 /init
root 7 0.0 0.0 892 80 ? S 12:20 0:01 /init
eduardo 2968 0.0 0.4 1256744 58664 pts/5 SLl+ 13:07 0:01 mongo
eduardo 21918 0.0 0.0 26364 8452 pts/0 Ss 15:49 0:00 /bin/bash --login
eduardo 22190 0.0 0.0 37800 3364 pts/0 R+ 16:22 0:00 ps aux
El primer proceso es init con su PID 1. 👀
Este comando despliega una tabla mucho más detalla (e inclusive extensa) del los procesos en el sistema. Con esta tabla podemos conocer la siguiente información.
- USER: usuario propietario del proceso.
- PID: Identifucadoer del proceso.
- %CPU: Porcentaje de tiempo que el proceso estuvo en ejecución desde que se inició.
- %MEM: Porcentaje de memoria RAM utilizada.
- VSZ: memoria virtual del proceso medida en KiB
- RSS: Cantidad de memoria utilizada por el proceso (Expresada en Kilobytes).
- TT: terminal que controla el proceso (tty)
- STAT: Código de estado del proceso.
- STARTED: Fecha de inicio del proceso.
- TIME: tiempo de CPU utilizado.
- COMMAND: comando con todos sus argumentos.
De igual forma, es importante detallar cada uno de los posibles estados para los procesos, que serían los siguientes (Esto para la columna STAT):
- R: En ejecución o listo para ser ejecutado.
- S: Suspendido. El proceso se encuentra a la espera de ser reanudado.
- D: El proceso se encuentra a la espera de I/O. T: Detenido.
- Z: Zombificado, un proceso difunto que aun no ha sido reclamado por el proceso padre.
Toda esta información, por supuesto, la podemos conocer en el manual del comando ps (man ps).
Monitorear procesos
Un comando que en lo particular me gusta mucho es top. Este comando nos permite visualizar, en tiempo real, el top de los procesos del sistema, actualizando la información de forma constante. Perfecto para todas aquellas ocasiones donde queremos conocer qué proceso esta causando un cuello de botella en nuestro servidor. 😵
$ top
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 1208 564 520 S 0.0 0.0 1:21.33 init
7 root 20 0 892 80 20 S 0.0 0.0 0:01.62 init
2968 eduardo 20 0 1256744 58664 33656 S 0.0 0.5 0:01.23 mongo
4684 eduardo 20 0 33672 8720 3040 S 0.0 0.1 0:04.40 tmux: server
4685 eduardo 20 0 26384 8516 3548 S 0.0 0.1 0:00.21 bash
4899 eduardo 20 0 26404 8348 3376 S 0.0 0.1 0:00.04 bash
5113 eduardo 20 0 26404 8400 3428 S 0.0 0.1 0:00.05 bash
5335 eduardo 20 0 1638868 150048 53448 S 0.0 1.2 1:13.90 mongod
5375 eduardo 20 0 1253672 52340 33048 S 0.0 0.4 0:00.80 mongo
Para salir, y regresar a la terminal, basta presionar la tecla q.
Control de procesos.
Listo, ya sabemos qué son los procesos y ya conocemos algunos atributos que nos permiten administrarlos, así que toca el turno de aprender a crearlos, y para ello tenemos 2 opciones:
- Foreground Processes
- Background Processes
Expliquemos cada uno de ellos. Comencemos con Foreground Processes. Por default cada proceso que creemos en terminal se ejecuta de esta manera, en primer plano, pudiendo así ingresar datos vía teclado y esperar una posible salida en pantalla.
Un ejemplo de Foreground Processes ocurre cuando ejecutamos nuestros scripts en terminal, pudiendo interactuar con el programa.
$ ruby saludar.rb
Ingresa tu nombre: Eduardo Ismael
Hola Eduardo Ismael, estamos en un proceso en primer plano.
Algo muy común ¿no lo crees? 😄 El único problema de ejecutar procesos en primer plano, es que mientras el programa se encuentre en ejecución, no podremos ejecutar ningún otro tipo de comando, debemos esperar a que el programa finalice. 😖
Ahora, para ejecutar procesos en segundo plano (Background Processes) basta con colocar el carácter ampersand (&) al final del comando.
La principal ventaja de ejecutar los procesos de esta forma, es la posibilidad de ejecutar otros comandos en terminal, independientemente si el programa en background ha finalizado o no.
Veamos un ejemplo. Ejecutemos en segundo plano el editor de texto nano.
nano &
[1] 529
Siempre que ejecutemos un proceso en background, inmediatamente se nos mostrará su PID en pantalla y retomaremos el control de la terminal. En mi caso se me indica que un nuevo job acaba de ser lanzado con el PID 22245.
Si listamos nuevamente, encontraremos nuestro proceso en background.
$ ps
PID TTY TIME CMD
260 pts/0 00:00:00 bash
529 pts/0 00:00:00 nano
530 pts/0 00:00:00 ps
El inconveniente de mandar los procesos a un segundo plano, es la nula interacción que tendremos con el programa, lo cual en ciertos escenarios (principalmente en tareas automatizadas) no debiese ser un gran problema.
Regreso a primer plano.
Si queremos regresar el proceso a primer plano, haremos uso del programa fg.
Primero listamos todos los Jobs lanzados en segundo plano desde la terminal.
$ jobs
[1]- Stopped nano
Y después, simplemente seleccionamos el proceso que queremos que se ejecute ahora en primer plano.
fg %1
Finalizar procesos.
Si queremos finalizar un proceso, podemos hacerlo de 2 formas diferentes. Si el proceso se encuentra en primer plano, basta con presionar control + c.
Por otro lado, si el proceso se encuentra en background haremos uso del programa kill. Basta con colocar la bandera -9 seguido por el PID del proceso.
kill -9 <PID>
Si te preguntas el por qué del -9, te lo explicaremos más adelante. 🤗
Deamons
Antes de continuar con los signals, debemos hablar sobre los Deamons, un tipo especial de proceso que se ejecuta segundo plano. 🎃
Los demonios son procesos que se ejecutan de forma automática en segundo plano y se caracterizan por no tener ninguna interacción con los usuarios. No poseen interfaz gráfica o textual y se encuentran en ejecución por siempre, como un servicio. Prácticamente pasan desapercibidos por la mayoría de nosotros.
Si intentamos matar aun procesos demonio, déjame decirte que esto no será posible, ya que regresarán a la vida. Claro, todo depende de las configuraciones y políticas del sistema. 🧟
SIGNALS.
Ya para finalizar el post hablemos sobre los signals. Los signals son la forma en la cual podemos comunicar procesos entre sí, mediante señales.
Para conocer que signals disponibles en nuestro sistema ejecutamos el siguiente comando.
$ kill -l
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
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU
25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH
29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN
35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4
39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12
47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14
51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10
55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6
59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
La señal que utilizamos anteriormente fue la número 9, una señal para finalizar un proceso.
Como podemos observar todos los signal posee un nombre, con sus primeros 3 caracteres SIG.
Sin bien existen una gran cantidad de signals disponibles, creo, desde mi poca o mucha experiencia administrando servidores, que el 99% de las veces haremos uso del signal SIGKILL para finalizar un proceso en segundo plano.
Si te interesa conocer más acerca de cada uno de lo signals, te comparto el un link que sin duda te será de mucha útilidad. ⚡