Linux: arranque y systemd
Qué ocurre desde que el núcleo termina de cargarse hasta que la máquina ofrece servicios: el proceso número uno, los niveles de ejecución del sistema clásico con sus enlaces de arranque y parada, y cómo lo ha sustituido systemd con sus unidades y sus targets.
El proceso número uno
Cuando el núcleo termina de cargarse no hay todavía ningún programa de usuario en marcha. Lo primero que lanza es un único proceso, y de ese proceso descienden todos los demás. Recibe el identificador 1 y su cometido es poner el sistema en el estado de funcionamiento que corresponda: montar los sistemas de ficheros, levantar la red y arrancar los servicios.
El ejecutable clásico era /sbin/init. En cualquier distribución actual esa ruta sigue existiendo, pero es un enlace simbólico al ejecutable de systemd, que vive en /lib/systemd/systemd o en /usr/lib/systemd/systemd. Es un detalle que confunde: ver /sbin/init no significa que la máquina use el sistema de inicio antiguo.
El proceso número uno tiene además una responsabilidad que se pregunta: adopta a los procesos que se quedan huérfanos. Cuando un proceso termina antes que sus hijos, esos hijos pasan a colgar del proceso 1, que es quien recoge su código de salida y evita que queden bloqueados en el sistema.
ps -p 1 -o pid,comm # qué programa es el proceso 1 en esta máquina
ls -l /sbin/init # a dónde apunta realmenteEl proceso 1 es el antepasado de todos los demás y el que adopta a los huérfanos. Si muere, el sistema no puede seguir: no hay a quién colgar los procesos.
Para el examen
PID 1: el antepasado de todos los procesos
Su papel especial: adopta a los procesos huérfanos
Si muere: el sistema no puede seguir
Los niveles de ejecución del sistema clásico
El sistema de inicio clásico define el estado de la máquina con un número, el nivel de ejecución o runlevel. Cambiar de nivel significa arrancar unos servicios y parar otros, y hay dos niveles que no son estados de trabajo sino transiciones: apagar y reiniciar.
| Nivel | Estado de la máquina |
|---|---|
| 0 | Apagado. Se para todo y se corta la alimentación |
| 1 | Monousuario: solo root, sin red ni servicios. Para reparar |
| 2 | Multiusuario |
| 3 | Multiusuario, típicamente con red y sin entorno gráfico |
| 4 | Libre, sin uso asignado por la distribución |
| 5 | Multiusuario con entorno gráfico |
| 6 | Reinicio |
El nivel monousuario es la herramienta de rescate por excelencia: arranca con lo mínimo, sin red y sin servicios, para poder reparar un sistema que no llega a levantar del todo o para cambiar una contraseña olvidada de administrador.
Cuidado con el nivel por defecto, que es un dato que se pregunta mal a menudo. No es universal: depende de la distribución. En la familia Debian el valor tradicional es el 2; en la familia Red Hat clásica es el 3 sin entorno gráfico y el 5 con él. Responder un número como si valiera para todas es un error.
0 apaga y 6 reinicia. Confundirlos es el fallo típico, y en un examen basta recordar que el 0 es el final de la escala hacia abajo.
Para el examen
Nivel 0: apagar
Nivel 1: monousuario
Nivel 6: reiniciar
Fallo típico: confundir el 0 con el 6
Los guiones de servicio y los directorios rc
En el sistema clásico, cada servicio se gobierna con un guion de shell guardado en /etc/init.d. Ese guion sabe arrancar, parar y consultar el estado de su servicio, y es lo que hay detrás del comando service de toda la vida.
Lo que decide qué guiones se ejecutan en cada nivel no está dentro del guion, sino fuera: hay un directorio por nivel, de /etc/rc0.d a /etc/rc6.d, más /etc/rcS.d para la fase inicial. Dentro de cada directorio no hay copias de los guiones, sino enlaces simbólicos a los de /etc/init.d.
El nombre del enlace lleva toda la información en dos piezas. La primera letra dice qué hacer al entrar en ese nivel, y el número que va detrás fija el orden, de menor a mayor. Así se resuelve el problema de las dependencias sin ningún mecanismo adicional: si la base de datos tiene que estar lista antes que el servidor de aplicaciones, se le da un número más bajo.
| Pieza del nombre | Significado |
|---|---|
| Letra S inicial | Iniciar el servicio al entrar en este nivel |
| Letra K inicial | Parar (kill) el servicio al entrar en este nivel |
| Número de dos cifras | Orden de ejecución: primero los más bajos |
| Resto del nombre | El servicio, que es el destino del enlace en /etc/init.d |
# un directorio de nivel es una lista ordenada de enlaces
/etc/rc3.d/S10postgresql -> /etc/init.d/postgresql
/etc/rc3.d/S20apache2 -> /etc/init.d/apache2
/etc/rc3.d/K10tomcat -> /etc/init.d/tomcatS arranca, K para, y el número ordena. En ese ejemplo la base de datos sube antes que el servidor web porque lleva el 10 y no el 20.
Para el examen
Prefijo S: arranca el servicio
Prefijo K: lo para
El número que sigue: fija el orden
systemd y los targets
systemd sustituyó al sistema clásico en prácticamente todas las distribuciones. El cambio de fondo es que ya no ejecuta guiones en cadena, sino que conoce las dependencias entre servicios y puede arrancar en paralelo todo lo que no dependa de nada, lo que acorta mucho el arranque.
El primer cambio visible es de vocabulario: los estados ya no se llaman runlevels ni se identifican por un número, sino targets identificados por un nombre. La equivalencia no es exacta pero se pregunta tal cual.
| Nivel clásico | Target de systemd |
|---|---|
| 0 | poweroff.target |
| 1 | rescue.target |
| 2, 3 y 4 | multi-user.target |
| 5 | graphical.target |
| 6 | reboot.target |
Un target no es un número de estado, es un punto de sincronización: un conjunto de unidades que tienen que estar levantadas para considerar alcanzado ese estado. Por eso varios niveles antiguos caen en el mismo target, y por eso un sistema puede tener targets propios que no corresponden a ningún runlevel.
systemctl get-default # a qué target arranca la máquina
systemctl set-default multi-user.target
systemctl isolate rescue.target # cambiar de estado ahora mismo
systemctl list-units --type=targetLos targets van por nombre y no por número, y no hay correspondencia uno a uno: los niveles 2, 3 y 4 desembocan todos en multi-user.target.
Para el examen
Cómo se identifican: por nombre, no por número
Correspondencia con los runlevels: no es uno a uno
Niveles 2, 3 y 4: desembocan todos en multi-user.target
Las unidades de systemd
La pieza básica de systemd no es el servicio sino la unidad, un fichero de configuración que describe algo que el sistema puede gestionar. La extensión dice de qué tipo es, y esa lista es lo primero que hay que saber para entender cualquier salida de systemctl.
| Extensión | Qué describe |
|---|---|
| .service | Un servicio o demonio |
| .target | Un conjunto de unidades: un punto de sincronización |
| .socket | Un puerto o socket que, al recibir tráfico, arranca su servicio |
| .timer | Una ejecución programada, la alternativa de systemd a cron |
| .mount | Un punto de montaje |
| .device | Un dispositivo reconocido por el sistema |
Las unidades que trae la distribución viven en /lib/systemd/system y no se tocan. Las propias, y las modificaciones locales de las anteriores, van en /etc/systemd/system, que tiene prioridad. Esa separación es lo que permite que una actualización del paquete no borre los cambios del administrador.
Al editar o añadir una unidad, systemd no se entera solo: hay que pedirle que relea la configuración. Olvidarlo es la causa más frecuente de que un cambio no surta efecto.
systemctl daemon-reload # releer las unidades tras editarlas
systemctl list-units --type=service --state=running
systemctl mask apache2 # impedir que se arranque, ni a mano
systemctl unmask apache2
systemctl list-unit-files # qué hay instalado y si está habilitadoHay tres verbos que se parecen y no son lo mismo. stop para el servicio ahora. disable evita que arranque en el próximo inicio, pero deja arrancarlo a mano. mask va más lejos: enmascara la unidad, de forma que ni siquiera un intento manual la levanta, y es lo que se usa cuando un servicio no debe correr bajo ningún concepto.
stop actúa ahora, disable actúa en el siguiente arranque y mask bloquea la unidad por completo. Si un servicio se niega a arrancar sin dar error claro, lo primero es mirar si está enmascarado.
Para el examen
stop: actúa ahora
disable: actúa en el siguiente arranque
mask: bloquea la unidad por completo
Diagnóstico: si un servicio no arranca sin error claro, mirar si está enmascarado