1.4 Hilos, servicios y procesos huérfanos o zombis¶
La memoria de un proceso¶
Cada proceso tiene su propio espacio de memoria, que el sistema divide en cuatro zonas:
| Zona | Qué guarda |
|---|---|
| Código | Las instrucciones del programa. Solo se leen, no se modifican |
| Datos | Las variables globales y las estáticas, que existen mientras el proceso vive |
| Montículo (heap) | La memoria que el programa pide mientras se ejecuta: cada objeto o lista que se crea |
| Pila (stack) | Las llamadas a funciones en curso, con sus parámetros y sus variables locales |
El sistema impide que un proceso lea o escriba en la memoria de otro. Es una ventaja: si un programa falla, no estropea a los demás. Y un inconveniente: para que dos procesos se comuniquen hace falta pasar por el sistema operativo.
PID, PPID y el árbol de procesos¶
Todo proceso lo crea otro, su padre. Cada proceso guarda el PID de quien lo creó, el PPID (Parent PID), y así se forma un árbol de procesos. En Linux el primero que arranca es systemd (o init), con PID 1, y todos descienden de él; en Windows, la mayoría de lo que abres con doble clic cuelga de explorer.exe.
En 1.1 viste cómo se crea un hijo y se consulta su PID; ahora interesa de quién es hijo. Esta demostración comprueba que el PPID del hijo es el PID del padre. Se ha ejecutado de verdad (Windows); los PID concretos no se muestran porque cambian cada vez.
import multiprocessing
import os
def hijo(pid_padre):
print(" Hijo: mi PID es distinto del del padre:", os.getpid() != pid_padre)
print(" Hijo: mi PPID (el PID de mi padre) coincide con el PID del padre:", os.getppid() == pid_padre)
if __name__ == "__main__":
proceso = multiprocessing.Process(target=hijo, args=(os.getpid(),))
proceso.start()
proceso.join()
print("El padre sabe que el hijo terminó con código:", proceso.exitcode)
Salida:
Hijo: mi PID es distinto del del padre: True
Hijo: mi PPID (el PID de mi padre) coincide con el PID del padre: True
El padre sabe que el hijo terminó con código: 0
Verlo en tu equipo (sin ejecutar aquí)
En Windows, el Administrador de tareas, en la pestaña Detalles, muestra el PID; el PPID se ve con Get-CimInstance Win32_Process | Select-Object ProcessId, ParentProcessId, Name en PowerShell. En Linux, ps -ef muestra PID y PPID, y pstree dibuja el árbol.
Hilos¶
Un hilo (thread) es un flujo de ejecución dentro de un proceso. Todo proceso tiene al menos uno; puede tener varios, como un procesador de textos que atiende el teclado en un hilo, revisa la ortografía en otro y guarda una copia automática en un tercero.
| Proceso | Hilo | |
|---|---|---|
| Memoria | Propia y aislada | Compartida con los demás hilos del mismo proceso (solo la pila es propia) |
| Coste de crearlo | Alto: el sistema reserva memoria y crea su BCP | Bajo |
| Comunicación | Hace falta un mecanismo del sistema operativo | Directa, a través de variables compartidas |
| Si falla | No afecta a los demás procesos | Puede hacer caer todo el proceso |
Esta demostración lo enseña con una lista compartida: un hilo la modifica y se nota; un proceso hijo la modifica y el padre no se entera, porque tiene su propia copia en su propia memoria.
import multiprocessing
import threading
datos = []
def tarea_hilo():
datos.append("lo añade el hilo")
def tarea_proceso():
datos.append("lo añade el proceso hijo")
print(" Dentro del proceso hijo, datos =", datos)
if __name__ == "__main__":
t = threading.Thread(target=tarea_hilo)
t.start()
t.join()
print("Después del hilo: ", datos)
p = multiprocessing.Process(target=tarea_proceso)
p.start()
p.join()
print("Después del proceso: ", datos)
Salida:
Dentro del proceso hijo, datos = ['lo añade el proceso hijo']
Después del hilo: ['lo añade el hilo']
Después del proceso: ['lo añade el hilo']
El hilo añadió su texto a la misma lista que ve el programa principal. El proceso hijo añadió el suyo a otra lista (en Windows el hijo ni siquiera hereda la lista del padre; en Linux partiría de una copia): en los dos casos, el padre no ve el cambio.
¿Procesos o hilos?
Se usan procesos cuando se quiere aislamiento (que un fallo no tumbe al resto) o ejecutar otro programa. Se usan hilos cuando se quiere hacer varias cosas dentro del mismo programa compartiendo datos con rapidez; pero entonces hay que sincronizar los accesos, como se vio en la sección crítica. Cambiar de un hilo a otro del mismo proceso también es más barato, porque no hay que cambiar de memoria.
Servicios (demonios)¶
Un servicio (en Linux, demonio o daemon) es un proceso que:
- se ejecuta en segundo plano, sin ventana ni interacción con el usuario;
- normalmente arranca con el sistema y sigue funcionando aunque nadie haya iniciado sesión;
- atiende peticiones: imprimir, conexiones remotas, consultas a una base de datos, páginas web.
Ejemplos: la cola de impresión, el servidor SSH (sshd), un servidor web o MySQL.
| Sistema | Ver y gestionar servicios |
|---|---|
| Windows | services.msc, o en PowerShell Get-Service y Restart-Service Spooler |
| Linux | systemctl status ssh, systemctl restart ssh |
| macOS | launchctl list |
Procesos huérfanos y zombis¶
Cuando un proceso hijo termina, deja un código de salida (0 si todo fue bien) que el padre tiene que recoger. Según quién termina primero y quién recoge, aparecen dos casos:
| Caso | Qué pasa | ¿Es un problema? |
|---|---|---|
| Huérfano | El padre termina antes que el hijo. El hijo sigue funcionando y el sistema lo adopta: pasa a ser hijo del proceso con PID 1 | No. Muchos servicios se lanzan así a propósito |
| Zombi | El hijo ya ha terminado, pero el padre todavía no ha recogido su código de salida | Un zombi no gasta CPU ni memoria, pero ocupa un PID y una entrada en la tabla de procesos. Si se acumulan miles, el sistema se queda sin PID. En Linux, ps lo muestra con la letra Z o con <defunct> |
No confundir
Un proceso que se queda bloqueado para siempre esperando un dato que nunca llega no es un zombi: sigue vivo y es un error del programa. El zombi, en cambio, ya está muerto; solo falta que su padre lo recoja (con wait, en Linux).
Por qué no hay ejemplo ejecutado
Los zombis y los huérfanos son un comportamiento de Linux y macOS. En Windows no se pueden provocar de la misma forma, y esta web solo muestra lo que se ha ejecutado de verdad.
Qué recordar¶
- La memoria de un proceso tiene código, datos, montículo y pila, y es privada.
- El PPID es el PID del padre; los procesos forman un árbol.
- Los hilos comparten la memoria del proceso; los procesos, no.
- Un servicio es un proceso en segundo plano que atiende peticiones.
- Huérfano: el padre terminó antes. Zombi: el hijo terminó y el padre no recogió su código.