Has compilado tu aplicación con Yocto. Ahora necesitas que se inicie automáticamente en el arranque. En Linux embebido, eso significa crear un servicio systemd.
Pero hacer que los servicios systemd funcionen correctamente en Yocto requiere que tres cosas estén alineadas: la configuración de la distribución, la receta y el archivo de servicio. Si falta alguna de ellas, tu servicio falla silenciosamente en el inicio.
Esta guía cubre todo desde añadir servicios systemd hasta 7 errores comunes, basada en Yocto Scarthgap 5.0 LTS (systemd 255).
Habilitar systemd con INIT_MANAGER
Yocto usa sysvinit como sistema init por defecto. Necesitas cambiar explícitamente a systemd.
Enfoque INIT_MANAGER (Zeus 3.0+)
Añade una sola línea a conf/local.conf o conf/distro/mydistro.conf.
# conf/local.conf
INIT_MANAGER = "systemd"
Esta sola línea carga init-manager-systemd.inc, que configura automáticamente lo siguiente.
# Contenido de init-manager-systemd.inc (rama Scarthgap)
DISTRO_FEATURES:append = " systemd usrmerge"
DISTRO_FEATURES_BACKFILL_CONSIDERED:append = " sysvinit"
VIRTUAL-RUNTIME_init_manager ??= "systemd"
VIRTUAL-RUNTIME_initscripts ??= "systemd-compat-units"
VIRTUAL-RUNTIME_login_manager ??= "shadow-base"
VIRTUAL-RUNTIME_dev_manager ??= "systemd"
ROOT_HOME ?= "/root"
El enfoque legacy (Pre-Zeus)
Antes de Zeus, tenías que configurar manualmente las variables DISTRO_FEATURES y VIRTUAL-RUNTIME_* en local.conf. Esto sigue funcionando pero el enfoque de una línea con INIT_MANAGER es más simple.
Verificar la configuración
# Verificar que systemd esté en DISTRO_FEATURES
bitbake-getvar DISTRO_FEATURES | grep systemd
Escribir una receta de servicio systemd
Para añadir un servicio systemd en Yocto, escribe una receta que herede la bbclass systemd.
Ejemplo de receta completa
# meta-mylayer/recipes-apps/myapp/myapp_1.0.bb
SUMMARY = "My custom application"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI = "file://myapp.sh \
file://myapp.service"
S = "${WORKDIR}"
inherit systemd
SYSTEMD_SERVICE:${PN} = "myapp.service"
SYSTEMD_AUTO_ENABLE:${PN} = "enable"
do_install() {
install -d ${D}${bindir}
install -m 0755 ${WORKDIR}/myapp.sh ${D}${bindir}/myapp
install -d ${D}${systemd_system_unitdir}
install -m 0644 ${WORKDIR}/myapp.service ${D}${systemd_system_unitdir}/
}
FILES:${PN} += "${systemd_system_unitdir}/myapp.service"
Variables clave
| Variable | Propósito | Valor predeterminado |
|---|---|---|
SYSTEMD_SERVICE:${PN} | Nombre(s) de archivo(s) de servicio a gestionar | ${PN}.service |
SYSTEMD_AUTO_ENABLE:${PN} | Si se habilita automáticamente | enable |
SYSTEMD_PACKAGES | Paquetes a gestionar con systemd | ${PN} |
Lo que inherit systemd hace automáticamente:
- Genera un script
postinstque ejecutasystemctl enable - Crea enlaces simbólicos bajo
multi-user.target.wants/ - Genera un script
prermpara la eliminación del paquete
Proteger tu receta para systemd
Para prevenir errores de compilación en distribuciones sin systemd, usa REQUIRED_DISTRO_FEATURES.
REQUIRED_DISTRO_FEATURES = "systemd"
Esto hace que la receta se salte cuando DISTRO_FEATURES no incluye systemd. Si otra receta depende de ella, se genera un error de compilación, facilitando rastrear la causa raíz.
Añadir a una receta existente vía bbappend
Si necesitas añadir un servicio systemd a una receta existente, usa un archivo bbappend. Consulta el Patrón 3 en la guía práctica de bbappend para los detalles. Ten en cuenta que inherit systemd no puede usarse en archivos .bbappend — la receta base .bb ya debe heredar la clase systemd.
Diseño de archivos de servicio para embebidos
Cómo escribes el archivo de servicio afecta directamente la fiabilidad del arranque. Estos son los puntos clave para Linux embebido.
Estructura básica
# myapp.service
[Unit]
Description=My Application Service
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/bin/myapp
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Sección [Unit]
After= controla el orden de arranque. Un error común con servicios que dependen de la red es usar After=network.target.
- network.target — indica que los dispositivos de red han sido configurados. No garantiza que se haya asignado una dirección IP
- network-online.target — indica que la red es utilizable. Usa esto cuando tu servicio necesite hacer llamadas API o conexiones de red
Sección [Service]
Type= especifica cómo se inicia el proceso.
| Tipo | Caso de uso |
|---|---|
simple | Proceso en primer plano (predeterminado) |
forking | Daemon que se bifurca al fondo |
oneshot | Script que se ejecuta una vez y termina |
Para embebidos, configura Restart=on-failure con RestartSec=5 (reintentar después de 5 segundos) para habilitar la recuperación automática de caídas.
Permisos
Instala los archivos de servicio con 0644. Añadir permisos de ejecución (0755) puede hacer que systemd emita advertencias.
# Correcto
install -m 0644 ${WORKDIR}/myapp.service ${D}${systemd_system_unitdir}/
# Incorrecto (permiso de ejecución es innecesario)
install -m 0755 ${WORKDIR}/myapp.service ${D}${systemd_system_unitdir}/
Recetas con Timer y Socket Activation
Las fortalezas de systemd van más allá de los servicios. Los timers gestionan tareas periódicas, y la activación por socket habilita el inicio bajo demanda.
Unidades Timer
Usa timers de systemd en lugar de cron para mantener las dependencias y logs integrados con systemd.
# myapp.timer
[Unit]
Description=Run myapp periodically
[Timer]
OnBootSec=1min
OnUnitActiveSec=1h
Persistent=true
[Install]
WantedBy=timers.target
# myapp.service (activado por el timer)
[Unit]
Description=My Application Task
[Service]
Type=oneshot
ExecStart=/usr/bin/myapp --run-task
En la receta, lista ambos archivos en SYSTEMD_SERVICE.
SRC_URI = "file://myapp.sh \
file://myapp.service \
file://myapp.timer"
SYSTEMD_SERVICE:${PN} = "myapp.service myapp.timer"
do_install() {
install -d ${D}${bindir}
install -m 0755 ${WORKDIR}/myapp.sh ${D}${bindir}/myapp
install -d ${D}${systemd_system_unitdir}
install -m 0644 ${WORKDIR}/myapp.service ${D}${systemd_system_unitdir}/
install -m 0644 ${WORKDIR}/myapp.timer ${D}${systemd_system_unitdir}/
}
FILES:${PN} += "${systemd_system_unitdir}/myapp.service \
${systemd_system_unitdir}/myapp.timer"
Activación por socket
La activación por socket retrasa el inicio del servicio hasta que llega una conexión real. Esto es efectivo para dispositivos embebidos con recursos limitados.
# myapp.socket
[Unit]
Description=My Application Socket
[Socket]
ListenStream=8080
Accept=no
[Install]
WantedBy=sockets.target
# myapp.service (iniciado por el socket)
[Unit]
Description=My Application
Requires=myapp.socket
[Service]
Type=simple
ExecStart=/usr/bin/myapp
La sintaxis de la receta es la misma que para los timers. Lista tanto el servicio como el socket en SYSTEMD_SERVICE.
SYSTEMD_SERVICE:${PN} = "myapp.service myapp.socket"
7 errores comunes y soluciones
Estos son los problemas más frecuentes reportados en las listas de correo y foros de Yocto.
1. systemd no está en DISTRO_FEATURES
El error más común. Aunque tengas inherit systemd en tu receta, si la configuración de tu distribución no tiene systemd habilitado, nada funciona.
# Verificar
bitbake-getvar DISTRO_FEATURES | grep systemd
# Solución: añadir a conf/local.conf
INIT_MANAGER = "systemd"
2. Falta WantedBy en la sección [Install]
Si el archivo de servicio no tiene sección [Install] o le falta WantedBy=, systemctl enable no hace nada. No se crea ningún enlace simbólico, y el servicio no se iniciará automáticamente.
# Obligatorio
[Install]
WantedBy=multi-user.target
3. Falta :$ en SYSTEMD_SERVICE
# Incorrecto (sin calificador de paquete)
SYSTEMD_SERVICE = "myapp.service"
# Correcto
SYSTEMD_SERVICE:${PN} = "myapp.service"
SYSTEMD_SERVICE es una variable por paquete. Sin :${PN}, la bbclass systemd no puede generar correctamente el script postinst.
4. Instalar en /etc/systemd/system/
# Incorrecto (/etc/ es para overrides del usuario)
install -d ${D}/etc/systemd/system/
# Correcto
install -d ${D}${systemd_system_unitdir}
# → /usr/lib/systemd/system/
/etc/systemd/system/ es donde los usuarios hacen overrides locales de unidades. Las unidades instaladas por paquetes pertenecen a ${systemd_system_unitdir} (/usr/lib/systemd/system/).
5. Esperar disponibilidad de red con network.target
# network.target: indica que los dispositivos de red están configurados
# ≠ la red está realmente utilizable
# Cuando necesitas conectividad de red real
[Unit]
After=network-online.target
Wants=network-online.target
network.target solo significa que las interfaces han sido configuradas. No garantiza la asignación de IP ni la resolución DNS.
6. Permisos de ejecución en archivos de servicio
# Incorrecto (systemd puede emitir advertencias)
install -m 0755 ${WORKDIR}/myapp.service ${D}${systemd_system_unitdir}/
# Correcto
install -m 0644 ${WORKDIR}/myapp.service ${D}${systemd_system_unitdir}/
Los archivos de servicio son archivos de configuración, no ejecutables. 0644 es el permiso correcto.
7. Logs de journald perdidos al reiniciar
Por defecto, journald almacena los logs en RAM (/run/log/journal/). Los logs desaparecen al reiniciar.
Para persistir los logs, crea el directorio /var/log/journal/ en tu receta.
do_install:append() {
install -d ${D}${localstatedir}/log/journal
}
FILES:${PN} += "${localstatedir}/log/journal"
Para dispositivos embebidos, considera la frecuencia de escritura en Flash. Las escrituras frecuentes de logs acortan la vida útil del Flash. Establece límites con SystemMaxUse= y MaxRetentionSec= en journald.conf.
Conclusión
Hacer que los servicios systemd funcionen en Yocto requiere tres cosas.
- Configuración de distribución: Habilitar con
INIT_MANAGER = "systemd" - Receta:
inherit systemdy configurarSYSTEMD_SERVICE:${PN} - Archivo de servicio: Incluir
WantedBy=multi-user.targeten la sección[Install]
La activación por timer y socket te permite integrar tareas periódicas e inicio bajo demanda en systemd también.
La mayoría de los errores se reducen a configuración faltante. Cuando las cosas no funcionan, empieza verificando DISTRO_FEATURES con bitbake-getvar y ejecutando systemctl status myapp en el dispositivo. La guía de depuración de errores de compilación cubre estas herramientas en detalle.
Para una inmersión más profunda en recetas systemd y desarrollo de recetas Yocto, estos libros son referencias sólidas.