← Fundamentos de Sistemas Operativos Bloque 5 · El proceso de arranque · Tema 22 de 34

Tema 22

El primer proceso: init, systemd y el espacio de usuario

El kernel está en marcha y acaba de ejecutar un programa. A partir de ese instante, todo lo que falta para tener un sistema utilizable —montar los discos, configurar la red, iniciar los servicios, mostrar una pantalla de acceso— es obra de ese programa y de los que él inicia. En este tema vemos quién es, cómo trabaja y cómo reconstruir lo que hizo.

1. El último eslabón

El tema 21 terminó en un punto preciso. El kernel había creado el proceso 1 y ejecutado en él el programa del initramfs; ese programa montó la partición raíz y, sin crear otro proceso, reemplazó su contenido por el de /sbin/init. La última línea que mostramos del registro era la de ese programa presentándose.

En ese momento el kernel ya hizo todo lo que le corresponde. Sabe administrar procesos, memoria, archivos y dispositivos. No sabe, ni debe saber, cómo se llama el equipo, qué discos hay que montar además de la raíz, qué dirección de red usar, qué servicios debe ofrecer esa máquina ni cómo se identifica a un usuario. Nada de eso es mecanismo: es política, y la decide el espacio de usuario, como planteamos en el tema 10.

El kernel delega todo eso en un único programa, el primero que ejecuta, y no vuelve a tomar iniciativa alguna. Desde aquí hasta el apagado se limita a lo que describimos en el tema 11: responder a llamadas al sistema, interrupciones y excepciones.

Según tu entorno

Lo de este tema se aplica a las distribuciones que usan systemd, que son casi todas. En WSL depende de la configuración: en las instalaciones recientes systemd está activado, y en las anteriores el proceso 1 es un programa propio de WSL. Lo sabrás con el primer comando de la sección siguiente. En un contenedor, el proceso 1 suele ser directamente la aplicación.

2. El proceso número 1

El proceso init es el primer proceso de usuario que crea el kernel. Tiene siempre el identificador 1, es el antecesor de todos los demás procesos y permanece en ejecución hasta que el sistema se apaga. Su tarea es llevar el sistema al estado de funcionamiento y mantenerlo en él.

terminal
$ ps -o pid,ppid,comm -p 1,2
    PID    PPID COMMAND
      1       0 systemd
      2       0 kthreadd
$ ls -l /sbin/init
lrwxrwxrwx 1 root root 22 abr 12 10:31 /sbin/init -> ../lib/systemd/systemd

La columna PPID indica el proceso padre, el que creó a cada uno. Los dos primeros procesos tienen un padre con número 0, que no corresponde a ningún proceso: los creó el kernel directamente. El 1 es el antecesor de todos los programas de usuario. El 2, kthreadd, es el antecesor de los hilos del kernel que vimos en el tema 11. Todo lo demás desciende de uno de ellos.

El segundo comando muestra que /sbin/init, el nombre tradicional que el kernel busca, es un enlace al ejecutable de systemd. El árbol completo se ve con pstree:

terminal (salida abreviada)
$ pstree -pT
systemd(1)─┬─NetworkManager(912)
           ├─cron(1042)
           ├─dbus-daemon(905)
           ├─gdm3(1120)───gdm-session-wor(1890)───…
           ├─sshd(1101)
           ├─systemd(2011)─┬─gnome-shell(2204)
           │               └─gnome-terminal-(3310)───bash(4102)───pstree(7655)
           ├─systemd-journal(388)
           ├─systemd-logind(930)
           └─systemd-udevd(431)

Cada proceso del sistema tiene una línea de ascendencia que termina en el 1. La del comando que acabamos de ejecutar es: pstree, hijo de la shell, hija de la terminal, hija de un segundo systemd que administra la sesión del usuario, hijo del primero.

El proceso 1 es un programa de usuario corriente, en el anillo 3, que hace llamadas al sistema como cualquier otro. Pero el kernel le da un trato que no da a ningún otro proceso:

ParticularidadMotivo
No puede terminar. Si lo hace, el kernel entra en pánico con el mensaje Attempted to kill init!Sin él no hay nadie que mantenga el sistema ni que lo apague ordenadamente.
Está protegido de las señales. Solo recibe aquellas para las que instaló un manejador; kill -9 1 no tiene efecto, ni siquiera como administrador.Evitar que se lo finalice por error.
Adopta a los huérfanos. Cuando un proceso termina antes que sus hijos, estos pasan a ser hijos del proceso 1.Todo proceso debe tener un padre que recoja su resultado al terminar.

La primera fila es el pánico que mencionamos en el tema 18, en otra de sus variantes. La tercera se entiende mejor con el ciclo de vida de los procesos, que vemos en el tema 26.

3. De init a systemd

Durante treinta años, el proceso 1 de los sistemas Unix fue un programa pequeño llamado simplemente init, en una versión que se conoce como SysV init por el Unix del que proviene. Su método era directo: ejecutaba, uno tras otro y en orden alfabético, los scripts de shell de un directorio. Cada script iniciaba un servicio.

El diseño era fácil de entender, y tenía limitaciones que se volvieron serias cuando los equipos empezaron a tener decenas de servicios y dispositivos que se conectan y desconectan en marcha:

SysV initsystemd
Un servicio se describe conUn script de shell, con su lógica propiaUn archivo de texto declarativo
Orden de inicioSecuencial, fijado por un número en el nombre del scriptEn paralelo, calculado a partir de las dependencias declaradas
Después de iniciar un servicioSe desentiende de élLo vigila, y puede reiniciarlo si termina
Detener un servicioConfía en que el script sepa qué procesos finalizarConoce con certeza todos los procesos del servicio
Mensajes de los serviciosCada servicio con su archivo y su formatoReunidos en un registro común
Estados del sistemaNiveles de ejecución, numerados del 0 al 6Targets, con nombre

systemd apareció en 2010 y en pocos años fue adoptado por casi todas las distribuciones. Tomó ideas de launchd, el equivalente de macOS. La cuarta fila de la tabla se apoya en un mecanismo del kernel de Linux que permite agrupar procesos de manera que ninguno pueda salirse del grupo; es el mismo sobre el que se construyen los contenedores del tema 23.

La adopción fue polémica. systemd no se limita a iniciar servicios: incluye el registro, la gestión de dispositivos, la de sesiones y la de red, entre otras cosas. Sus críticos ven en eso una ruptura con la tradición de Unix de programas pequeños que hacen una sola cosa. Algunas distribuciones usan alternativas más simples, como OpenRC en Alpine. Aquí estudiamos systemd porque es el que encontrarás.

4. Las unidades

systemd administra más que servicios. Todo lo que maneja se describe con un mismo tipo de archivo, y cada cosa administrada se llama unidad. El tipo se reconoce por la extensión del nombre:

TipoRepresentaEjemplo
.serviceUn programa que se inicia y se supervisa.cron.service
.targetUn punto de referencia que agrupa otras unidades.multi-user.target
.mountUn sistema de archivos que debe montarse.boot-efi.mount
.deviceUn dispositivo detectado por el kernel.dev-nvme0n1p2.device
.socketUn punto de conexión. El servicio asociado se inicia cuando alguien se conecta.ssh.socket
.timerUna tarea que se ejecuta a una hora o cada cierto tiempo.apt-daily.timer

Los archivos están en tres lugares, que se consultan en orden de prioridad:

DirectorioContenidoQuién escribe allí
/etc/systemd/systemUnidades y ajustes propios de este equipo. Tienen prioridad.El administrador
/run/systemd/systemUnidades generadas al arrancar. Desaparecen al apagar.El propio sistema
/usr/lib/systemd/systemLas unidades que traen los paquetes instalados.El gestor de paquetes

La separación resuelve un problema práctico. Si el administrador modificara los archivos del tercer directorio, la siguiente actualización del paquete borraría sus cambios. Los cambios se ponen en el primero, que ninguna actualización toca.

5. Anatomía de un servicio

Un archivo de unidad es un texto dividido en secciones, cada una con pares de nombre y valor. Este es el de cron, el servicio de tareas programadas que apareció en la lista del tema 10:

terminal
$ systemctl cat cron.service
# /usr/lib/systemd/system/cron.service
[Unit]
Description=Regular background program processing daemon
Documentation=man:cron(8)
After=remote-fs.target nss-user-lookup.target

[Service]
EnvironmentFile=-/etc/default/cron
ExecStart=/usr/sbin/cron -f -P $EXTRA_OPTS
KillMode=process
Restart=on-failure

[Install]
WantedBy=multi-user.target
SecciónLíneaSignificado
[Unit]DescriptionEl texto que se muestra en los listados y en el registro.
AfterSi esas otras unidades van a iniciarse, esta debe esperar a que lo hagan.
[Service]ExecStartEl comando que inicia el servicio. Es la línea esencial.
RestartQué hacer si el programa termina: aquí, volver a iniciarlo si terminó con error.
[Install]WantedByA qué target se asocia el servicio cuando se lo habilita para el arranque.

El archivo no dice cómo iniciar el servicio, verificar que arrancó o detenerlo: dice qué programa es y bajo qué condiciones debe ejecutarse. De lo demás se ocupa systemd, igual para todos. Es lo que significa «declarativo» en la tabla de la sección 3.

El estado de una unidad se consulta con systemctl status, que no requiere privilegios:

terminal
$ systemctl status cron
● cron.service - Regular background program processing daemon
     Loaded: loaded (/usr/lib/systemd/system/cron.service; enabled; preset: enabled)
     Active: active (running) since Mon 2026-10-12 09:14:07 -03; 2h 31min ago
       Docs: man:cron(8)
   Main PID: 1042 (cron)
      Tasks: 1 (limit: 18912)
     Memory: 1.2M (peak: 2.9M)
        CPU: 84ms
     CGroup: /system.slice/cron.service
             └─1042 /usr/sbin/cron -f -P

Las dos palabras resaltadas responden preguntas distintas, y confundirlas es el error más común al empezar:

enabled / disabledactive / inactive
Responde a¿Se iniciará en el próximo arranque?¿Está en ejecución ahora?
Se cambia consystemctl enable y disablesystemctl start y stop
EfectoPermanenteInmediato; no sobrevive al reinicio

Un servicio puede estar habilitado y detenido, o en marcha sin estar habilitado. Quien inicia un servicio con start y olvida enable lo descubre en el siguiente reinicio. El resto de la salida se lee con lo visto en otros temas: el PID del proceso principal, el tiempo de CPU acumulado —el que el kernel contabiliza en cada tic, según el tema 19— y, en CGroup, la lista de todos los procesos que pertenecen al servicio.

6. Los targets: el arranque como un grafo

systemd no sigue una lista. Cada unidad declara de cuáles depende y después de cuáles debe iniciarse, y con esas declaraciones se forma un grafo. El arranque consiste en recorrerlo, iniciando a la vez todo lo que no tenga dependencias pendientes. En un equipo con varios núcleos, decenas de servicios se inician en paralelo.

Hay que distinguir dos clases de relación, que son independientes:

DirectivaClaseSignificado
Requires=NecesidadEsta unidad necesita a la otra. Si la otra falla, esta no se inicia.
Wants=NecesidadConviene iniciar también la otra, pero si falla se continúa igual.
After=, Before=OrdenSi las dos van a iniciarse, en qué orden. No obliga a iniciar la otra.

Para dar estructura al grafo existen los targets: unidades que no ejecutan nada y sirven como puntos de referencia. Alcanzar un target significa que todo lo asociado a él está iniciado. Son los estados por los que pasa el sistema durante el arranque:

Los principales targets del arranque, en orden: sysinit, con los discos, dispositivos y el registro; basic, con los puntos de conexión y temporizadores; multi-user, con la red y los servicios; y graphical, con la pantalla de inicio de sesión sysinit discos, dispositivos, registro basic puntos de conexión, temporizadores multi-user red, servicios, acceso por terminal graphical pantalla de inicio de sesión
Cada target incluye al anterior. Un servidor se detiene en el tercero; un equipo de escritorio llega al cuarto.
terminal
$ systemctl get-default              # ¿hasta dónde arranca este equipo?
graphical.target
$ systemctl list-dependencies graphical.target | head -6
graphical.target
● ├─gdm.service
● ├─power-profiles-daemon.service
● └─multi-user.target
●   ├─cron.service
●   ├─NetworkManager.service

Ahora se entiende la línea WantedBy=multi-user.target del archivo de cron: al habilitarlo, queda anotado entre las unidades que ese target quiere iniciar. Y se entiende el parámetro systemd.unit=rescue.target del tema 21: le indica a systemd que se detenga en un target distinto del predeterminado, uno que solo incluye lo indispensable.

Los targets reemplazan a los niveles de ejecución de SysV init, y se conserva la equivalencia por compatibilidad:

Nivel antiguoTargetEstado del sistema
0poweroff.targetApagado.
1rescue.targetRecuperación: un solo usuario, sin red ni servicios.
3multi-user.targetSistema completo, sin entorno gráfico.
5graphical.targetSistema completo, con entorno gráfico.
6reboot.targetReinicio.

7. La secuencia del espacio de usuario

Con esos elementos, esto es lo que ocurre entre que systemd empieza a ejecutarse y aparece la pantalla de acceso. Aunque buena parte sucede en paralelo, las fases tienen un orden:

  1. systemd se inicia. Lee los archivos de unidad de los tres directorios y construye el grafo de dependencias a partir del target predeterminado. proceso 1
  2. Genera unidades a partir de la configuración clásica. Por cada línea de /etc/fstab, el archivo que enumera los sistemas de archivos del equipo, crea una unidad .mount. proceso 1
  3. Inicia el registro y la gestión de dispositivos. systemd-journald empieza a recoger mensajes, y systemd-udevd a atender los avisos del kernel. servicios
  4. Se completa la detección del hardware. udev recorre los dispositivos ya detectados y carga los módulos que faltan, usando los alias que vimos en el tema 24. El initramfs solo había cargado los necesarios para llegar al disco. servicios
  5. Se comprueba y se monta el resto de los sistemas de archivos. La raíz, que el kernel montó en solo lectura, se vuelve a montar con escritura. servicios
  6. Se alcanza sysinit.target y luego basic.target. Quedan establecidos el nombre del equipo, la hora, los puntos de conexión y los temporizadores. proceso 1
  7. Se inician los servicios, en paralelo: red, tareas programadas, acceso remoto y los que cada equipo tenga habilitados. servicios
  8. Se alcanza multi-user.target. En las terminales de texto queda esperando un programa que pide usuario y contraseña. proceso 1
  9. Se inicia el gestor de pantalla y se alcanza graphical.target. Aparece la pantalla de inicio de sesión. servicios
  10. Un usuario se identifica. Se inicia un segundo systemd, propio de ese usuario, que administra los programas de su sesión. sesión

El paso 5 cierra un cabo del tema 21: el parámetro ro hacía montar la raíz en solo lectura precisamente para que en este punto pudiera verificarse antes de permitir escrituras. Y el paso 10 explica el segundo systemd que apareció en la salida de pstree.

A partir de ahí el sistema está en régimen. systemd no termina: queda dormido, esperando que un servicio finalice, que se conecte un dispositivo, que venza un temporizador o que alguien le dé una orden con systemctl.

8. Experimento: un servicio propio

La mejor forma de entender una unidad es escribir una. El servicio será un programa mínimo, que anuncia su inicio y luego escribe una línea cada diez segundos:

latido.c
#include <stdio.h>
#include <unistd.h>

int main(void)
{
    setvbuf(stdout, NULL, _IOLBF, 0);       /* vaciar el búfer en cada línea */

    printf("iniciado con PID %d\n", (int)getpid());

    for (int n = 1; ; n++) {
        sleep(10);
        printf("sigo aqui (%d)\n", n);
    }
}

La línea resaltada es una aplicación directa del tema 15. Un servicio no tiene terminal: systemd conecta su salida estándar al registro del sistema. Como el destino no es una terminal, la biblioteca de C usaría el búfer completo, y los mensajes quedarían retenidos en la memoria del programa durante horas. Con setvbuf pedimos el modo por líneas. Es un error real y frecuente al convertir un programa en servicio: «funciona, pero no registra nada».

El archivo de unidad tiene lo mínimo:

latido.service
[Unit]
Description=Servicio de ejemplo del tema 22

[Service]
ExecStart=/usr/local/bin/latido
Restart=always
RestartSec=2

[Install]
WantedBy=multi-user.target
terminal
$ gcc -Wall -Wextra -g latido.c -o latido
$ sudo cp latido /usr/local/bin/
$ sudo cp latido.service /etc/systemd/system/
$ sudo systemctl daemon-reload                # que systemd relea los archivos de unidad
$ sudo systemctl start latido

$ journalctl -u latido -n 3 --no-pager
oct 12 11:52:10 equipo systemd[1]: Started latido.service - Servicio de ejemplo del tema 22.
oct 12 11:52:10 equipo latido[7731]: iniciado con PID 7731
oct 12 11:52:20 equipo latido[7731]: sigo aqui (1)

$ ps -o pid,ppid,comm -C latido
    PID    PPID COMMAND
   7731       1 latido

El programa está en ejecución sin terminal y sin que ningún usuario lo haya lanzado directamente. Su padre es el proceso 1. Y lo que escribe con printf aparece en el registro, con la fecha, el nombre del equipo y el PID añadidos por el sistema: el programa no tuvo que ocuparse de nada de eso.

Falta ver la supervisión. Finalizamos el proceso por la fuerza, simulando un fallo:

terminal
$ sudo kill -9 7731
$ journalctl -u latido -n 5 --no-pager
oct 12 11:53:02 equipo systemd[1]: latido.service: Main process exited, code=killed, status=9/KILL
oct 12 11:53:02 equipo systemd[1]: latido.service: Failed with result 'signal'.
oct 12 11:53:04 equipo systemd[1]: latido.service: Scheduled restart job, restart counter is at 1.
oct 12 11:53:04 equipo systemd[1]: Started latido.service - Servicio de ejemplo del tema 22.
oct 12 11:53:04 equipo latido[7790]: iniciado con PID 7790

El kernel avisó al proceso 1 de que su hijo había terminado, y por qué. systemd lo anotó, esperó los dos segundos indicados en RestartSec y lo inició de nuevo, con otro PID. Es la diferencia central con SysV init: el servicio no solo se inicia, se mantiene.

Por último, la habilitación para el arranque, y la limpieza:

terminal
$ sudo systemctl enable latido
Created symlink /etc/systemd/system/multi-user.target.wants/latido.service → /etc/systemd/system/latido.service.

$ sudo systemctl disable --now latido          # deshabilitar y detener
Removed "/etc/systemd/system/multi-user.target.wants/latido.service".
$ sudo rm /etc/systemd/system/latido.service /usr/local/bin/latido
$ sudo systemctl daemon-reload

La salida de enable revela que habilitar un servicio no tiene ningún misterio: es crear un enlace al archivo de unidad en un directorio cuyo nombre es el del target seguido de .wants. En el próximo arranque, al construir el grafo, systemd encontrará el enlace y sabrá que ese target quiere ese servicio.

9. El registro: journalctl

En el experimento usamos journalctl sin presentarlo. Es el comando que consulta el registro del sistema, que mantiene el servicio systemd-journald. Ese servicio reúne en un solo lugar mensajes de tres orígenes:

Los guarda en un formato binario con índices, lo que permite filtrar con rapidez por servicio, por fecha, por gravedad o por arranque:

ComandoMuestra
journalctl -bTodo lo registrado desde el arranque actual.
journalctl -b -1Lo del arranque anterior.
journalctl --list-bootsLa lista de arranques de los que hay registro.
journalctl -kSolo los mensajes del kernel.
journalctl -u cronSolo los de una unidad.
journalctl -p errSolo los de gravedad «error» o superior.
journalctl --since "10 min ago"Los de un intervalo de tiempo.
journalctl -fLos nuevos, a medida que llegan.
journalctl -n 20Los últimos veinte.

Las opciones se combinan: journalctl -b -u NetworkManager -p warning muestra las advertencias de ese servicio en este arranque. Para leer el registro completo hace falta ser administrador o pertenecer al grupo adm; cada usuario puede ver sin restricciones los mensajes de sus propios programas.

La relación con dmesg, que usamos desde el tema 11, se resume así:

dmesgjournalctl
Lee deEl búfer de mensajes, en la memoria del kernelArchivos en /var/log/journal
ContieneSolo mensajes del kernelKernel y espacio de usuario
Tras un reinicioSe pierdeSe conserva
Si el búfer se llenaLos mensajes nuevos pisan a los más antiguosNo aplica: ya están en el disco
DisponibleDesde el instante cero, antes de que exista ningún procesoDesde que se inicia el servicio de registro, que recoge también lo anterior

La tercera fila tiene una consecuencia práctica de primer orden. Si el equipo se colgó y hubo que reiniciarlo, dmesg ya no sirve: muestra el arranque nuevo. Los últimos mensajes del kernel antes del problema están en journalctl -k -b -1, siempre que hayan llegado a escribirse en el disco.

10. Leer el arranque

Con dmesg reconstruimos en el tema 21 la parte del kernel. El registro permite continuar esa lectura donde la dejamos. Pedimos las marcas de tiempo en segundos desde el arranque, como las del kernel, y filtramos los hitos:

terminal (líneas recortadas)
$ journalctl -b -o short-monotonic --no-pager | grep -E 'systemd\[1\]: (systemd 2|Reached target|Startup)'
[    2.391005] equipo systemd[1]: systemd 255.4-1ubuntu8 running in system mode (…)
[    5.321114] equipo systemd[1]: Reached target local-fs.target - Local File Systems.
[    5.792872] equipo systemd[1]: Reached target sysinit.target - System Initialization.
[    5.871251] equipo systemd[1]: Reached target basic.target - Basic System.
[    8.287140] equipo systemd[1]: Reached target network-online.target - Network is Online.
[    8.290307] equipo systemd[1]: Reached target multi-user.target - Multi-User System.
[    8.292226] equipo systemd[1]: Reached target graphical.target - Graphical Interface.
[    8.324418] equipo systemd[1]: Startup finished in 4.512s (firmware) + 2.210s (loader) + 2.391s (kernel) + 5.933s (userspace) = 15.046s.

La primera línea es la última que mostramos en el tema 21: los dos registros empalman. Las siguientes son los cuatro targets del diagrama de la sección 6, alcanzados en orden, y la final es el resumen que informa systemd-analyze. Este equipo tardó unos seis segundos en ir de systemd a un sistema completo: es la cifra de userspace.

El salto entre basic.target y multi-user.target llama la atención: casi dos segundos y medio en los que no aparece ningún hito. Para saber en qué se fueron hay dos comandos:

terminal
$ systemd-analyze blame | head -5
2.104s NetworkManager-wait-online.service
1.312s snapd.seeded.service
 843ms plymouth-quit-wait.service
 512ms dev-nvme0n1p2.device
 271ms NetworkManager.service

$ systemd-analyze critical-chain
graphical.target @5.901s
└─multi-user.target @5.899s
  └─NetworkManager-wait-online.service @3.790s +2.104s
    └─NetworkManager.service @3.512s +271ms
      └─basic.target @3.480s
ComandoInformaAdvertencia
systemd-analyze blameCuánto tardó cada unidad en iniciarse, de mayor a menor.Los tiempos no se suman: las unidades se inician en paralelo.
systemd-analyze critical-chainLa cadena de dependencias que determinó el tiempo total. Tras @, cuándo se inició cada unidad; tras +, cuánto tardó.Es la que hay que mirar para acortar el arranque.

La cadena crítica da la respuesta; sus tiempos se cuentan desde que systemd empezó, no desde el encendido. De los seis segundos, dos los consumió un servicio cuyo único trabajo es esperar a que la red esté conectada, para que los servicios que la necesitan no empiecen antes. No es un defecto, pero es una decisión: en un equipo que no necesita la red al arrancar, deshabilitarlo ahorra un tercio del tiempo. Es el margen de ajuste que anunciamos en el tema 21.

11. Cuando un servicio falla

La tabla de fallos del tema 21 terminaba con una fila que dejamos para este: la raíz se montó, el sistema de inicio arrancó, y algo posterior salió mal. Según la gravedad, hay tres situaciones:

SituaciónSíntomaCausa habitual
Falla un servicio no esencialEl sistema arranca con normalidad; algo no funciona.Un error de configuración de ese servicio.
Falla algo imprescindibleEl mensaje «You are in emergency mode» y una petición de la contraseña de administrador.Casi siempre, una línea de /etc/fstab que nombra un disco o una partición que ya no existe.
Termina el proceso 1Pánico del kernel: Attempted to kill init!El sistema de inicio dañado, o un parámetro init= que apunta a un programa que termina.

El modo de emergencia es el caso que más asusta y uno de los más fáciles de resolver. systemd no pudo montar un sistema de archivos declarado como necesario, y en lugar de seguir con un sistema incompleto se detiene y ofrece una shell. Se corrige o se comenta la línea errónea de /etc/fstab y se reinicia.

Para el primer caso, que es el cotidiano, el procedimiento tiene tres pasos:

terminal
$ systemctl --failed                           # 1. ¿qué unidades fallaron?
  UNIT            LOAD   ACTIVE SUB    DESCRIPTION
● nginx.service   loaded failed failed A high performance web server

$ systemctl status nginx                       # 2. ¿cómo terminó?
× nginx.service - A high performance web server
     Active: failed (Result: exit-code) since …
    Process: 2210 ExecStartPre=/usr/sbin/nginx -t (code=exited, status=1/FAILURE)

$ journalctl -u nginx -b -n 3 --no-pager       # 3. ¿qué dijo antes de terminar?
oct 12 09:14:09 equipo nginx[2210]: nginx: [emerg] unexpected "}" in /etc/nginx/nginx.conf:41
oct 12 09:14:09 equipo nginx[2210]: nginx: configuration file /etc/nginx/nginx.conf test failed
oct 12 09:14:09 equipo systemd[1]: nginx.service: Control process exited, code=exited, status=1/FAILURE

El tercer paso da la causa con archivo y número de línea. Lo que el servicio escribió en su salida de errores, que en una terminal habría pasado ante los ojos de alguien, quedó guardado: nadie miraba en el momento del arranque, y por eso existe el registro. Cuando ni siquiera el registro alcanza, queda el recurso del tema 16: ejecutar a mano el comando de ExecStart bajo strace.

12. El apagado

Apagar es también tarea del proceso 1, y es el arranque recorrido al revés. Importa hacerlo en orden por una razón que conocemos del tema 7: el kernel mantiene en memoria datos que todavía no escribió en el disco, y los servicios pueden tener trabajo a medio guardar.

  1. Alguien pide el apagado, con systemctl poweroff o desde el entorno gráfico. systemd toma como objetivo poweroff.target. proceso 1
  2. Detiene las unidades en orden inverso al de inicio. A cada servicio le envía una señal que le pide terminar, y le da un plazo para guardar su estado. servicios
  3. Finaliza por la fuerza a los que no terminaron dentro del plazo. Es el origen del mensaje «A stop job is running…» que a veces demora un apagado. proceso 1
  4. Desmonta los sistemas de archivos, lo que obliga a escribir en el disco todo lo pendiente. proceso 1
  5. Hace la llamada al sistema reboot, con la indicación de apagar o de reiniciar. kernel
  6. El kernel detiene los dispositivos y le pide al firmware que corte la alimentación, mediante las tablas ACPI que recibió durante el arranque. firmware

La secuencia empieza y termina en el mismo lugar que el arranque: en el firmware. Y explica por qué no hay que apagar un equipo cortando la corriente. Sin los pasos 2 a 4, lo que estaba en la memoria y no en el disco se pierde, y un sistema de archivos interrumpido a mitad de una modificación puede quedar en un estado incoherente.

13. Cierre del bloque

Con este tema queda completa la cadena que dibujamos al comenzar el tema 20. Cada eslabón resuelve una limitación del anterior y es reemplazado por el siguiente:

EslabónEstá enSabe hacerEntrega el control aTema
FirmwareUn chip de la placaInicializar el hardware; leer un archivo de una partición FATEl cargador de arranque20
CargadorLa partición EFILeer el sistema de archivos de Linux; cargar dos archivos en memoriaEl kernel21
Kernel/bootAdministrar la máquina; ejecutar un programaEl init del initramfs21
initramfsLa memoriaCargar módulos; encontrar y montar la raízEl init del sistema21
systemdLa partición raízMontar, configurar, iniciar y vigilar los serviciosNadie: permanece22

La última fila es la única distinta. Los cuatro primeros eslabones son andamios: hacen su parte y desaparecen. El quinto se queda, y es el primero que no se ejecuta con privilegio total. Con él termina el arranque y empieza el funcionamiento normal, el que estudiamos en los bloques 3 y 4.

El recorrido deja ver además un patrón que se repite en cada paso. El firmware tiene sus propios drivers; el cargador, los suyos; el initramfs, un espacio de usuario en miniatura. Cada etapa es una versión reducida de un sistema operativo, construida para poder cargar una versión más completa. Arrancar es reconstruir la máquina extendida del tema 2 una capa por vez, partiendo de un procesador que solo sabe buscar su primera instrucción en una dirección fija.

Hasta aquí estudiamos un sistema, Linux, y su manera de hacer las cosas. El bloque siguiente amplía la mirada: qué tipos de sistemas operativos existen y de qué otras maneras puede construirse un kernel.

14. Resumen

15. Preguntas de repaso

Intenta responder cada una antes de abrir la respuesta.

1. ¿Por qué el kernel no se ocupa de configurar la red o de iniciar los servicios?

Porque no son mecanismos, sino decisiones propias de cada equipo, y no requieren privilegios de hardware. El kernel ofrece los medios, y delega esas decisiones en programas de usuario, empezando por el primero que ejecuta.

2. ¿Qué indica que los procesos 1 y 2 tengan como padre al 0?

Que no los creó ningún otro proceso, sino el kernel directamente. El 1 es el antecesor de los procesos de usuario y el 2, el de los hilos del kernel.

3. Menciona tres particularidades del proceso 1.

No puede terminar: si lo hace, el kernel entra en pánico. Solo recibe las señales para las que instaló un manejador, de modo que no se lo puede finalizar con kill -9. Y adopta a los procesos cuyo padre terminó antes que ellos.

4. ¿Qué limitaciones de SysV init resolvió systemd?

El inicio secuencial, que desaprovechaba los equipos con varios núcleos; la falta de supervisión, por la que un servicio caído quedaba caído; la dificultad para saber qué procesos pertenecían a un servicio; y la dispersión de los mensajes en archivos distintos.

5. ¿Por qué las unidades propias se guardan en /etc/systemd/system y no en /usr/lib/systemd/system?

Porque el segundo directorio pertenece a los paquetes: una actualización sobrescribiría los cambios. El primero es del administrador, tiene prioridad y ninguna actualización lo modifica.

6. Un servicio aparece como disabled y active (running). ¿Qué significa?

Que está en ejecución ahora, porque alguien lo inició, pero no se iniciará en el próximo arranque. Para que lo haga hay que habilitarlo con systemctl enable.

7. ¿Qué diferencia hay entre Requires= y After=?

Requires= expresa necesidad: esta unidad no puede funcionar sin la otra. After= expresa solo orden: si las dos se inician, esta va después. Son independientes, y a menudo se usan juntas.

8. ¿Qué es un target, y a qué mecanismo antiguo reemplaza?

Una unidad que no ejecuta nada y sirve como punto de referencia: alcanzarla significa que todas las unidades asociadas están iniciadas. Reemplaza a los niveles de ejecución numerados de SysV init.

9. ¿Qué hace exactamente systemctl enable?

Crea un enlace al archivo de la unidad en el directorio .wants del target indicado en su línea WantedBy. En el arranque siguiente, systemd encuentra el enlace e incluye la unidad en el grafo.

10. ¿Por qué latido.c llama a setvbuf?

Porque la salida estándar de un servicio no es una terminal, y la biblioteca de C usaría el búfer completo: los mensajes quedarían en la memoria del proceso hasta acumular varios kilobytes. Con el modo por líneas, cada mensaje llega al registro en el momento.

11. Al finalizar latido con kill -9, vuelve a aparecer con otro PID. ¿Quién lo hace, y cómo se enteró?

systemd, que es su proceso padre. El kernel avisa al padre cuando un hijo termina; al tener la unidad Restart=always, systemd lo inicia de nuevo después del tiempo indicado en RestartSec.

12. El equipo se colgó y hubo que reiniciarlo. ¿Dónde buscas los últimos mensajes del kernel anteriores al problema?

En journalctl -k -b -1, que muestra los mensajes del kernel del arranque anterior. dmesg lee un búfer en memoria, que se perdió con el reinicio.

13. ¿Por qué los tiempos de systemd-analyze blame suman más que la duración del arranque?

Porque las unidades se inician en paralelo, y sus tiempos se superponen. Para saber qué determinó la duración total hay que mirar critical-chain, que muestra la cadena de dependencias más larga.

14. ¿Qué puede pasar si se apaga un equipo cortando la corriente?

Los servicios no guardan su estado, y los datos que el kernel tenía en memoria sin escribir se pierden. Un sistema de archivos interrumpido a mitad de una modificación puede quedar incoherente.

Para practicar

  1. Averigua qué programa es el proceso 1 de tu sistema y adónde apunta /sbin/init. Sigue con pstree -ps $$ la ascendencia de tu shell.
  2. Ejecuta sudo kill -9 1 y comprueba con ps -p 1 que no ocurrió nada. Explica por qué.
  3. Elige un servicio en ejecución, míralo con systemctl cat y localiza las líneas ExecStart, Restart y WantedBy. ¿Dónde está su archivo de unidad?
  4. Cuenta las unidades de cada tipo con systemctl list-units --type=service --no-legend | wc -l, cambiando el tipo por mount, socket y timer.
  5. Realiza el experimento de la sección 8 completo. Luego quita la línea de setvbuf, vuelve a instalar el programa y observa cuánto tarda en aparecer el primer mensaje en journalctl -u latido -f.
  6. En latido.service, cambia Restart=always por Restart=on-failure. Finaliza el proceso primero con kill -9 y luego con systemctl stop. ¿En qué caso se reinicia?
  7. Reconstruye el arranque de tu equipo con el comando de la sección 10 y anota cuándo se alcanzó cada target.
  8. Ejecuta systemd-analyze critical-chain. ¿Qué unidad determina la duración de tu arranque? ¿Es un trabajo o una espera?
  9. Compara sudo dmesg | head -3 con journalctl -k -b | head -5. Luego consulta journalctl --list-boots: ¿de cuántos arranques conserva registro tu sistema?
  10. Busca los errores del arranque actual con journalctl -b -p err. Elige uno e intenta determinar qué servicio lo produjo y si es grave.