script con `read` en bash mientras que el bucle causa un alto uso de CPU cuando se ejecuta como un servicio systemd

script con `read` en bash mientras que el bucle causa un alto uso de CPU cuando se ejecuta como un servicio systemd

Escribí un script para ejecutar acciones específicas condicionadas a eventos de entrada informados por un monitor de eventos, que se parece a

$ cat script.sh
-----------------------------------------------------------
#!/usr/bin/bash

stdbuf -oL /usr/bin/event_monitor | while IFS= read LINE
do
    something with $LINE
done

Cuando se ejecuta como un script desde una bashterminal, el script consume una cantidad normal de CPU y ejecuta la acción solo cuando se imprime una nueva línea. Sin embargo, cuando se ejecuta como un servicio con la siguiente configuración

$ cat event.service
-----------------------------------------------------------
[Unit]
Description=Actions upon events

[Service]
Type=simple
ExecStart=/path/to/script.sh

[Install]
WantedBy=default.target

El event_monitorcomando ahora se hace cargo de un núcleo lógico completo y stracerevela que no readse read()realiza nada con tanta frecuencia como lo permite el procesador:

$ strace -p $event_monitor_pid
-----------------------------------------------------------
read(0, "", 1)                          = 0
read(0, "", 1)                          = 0
read(0, "", 1)                          = 0
read(0, "", 1)                          = 0
read(0, "", 1)                          = 0
read(0, "", 1)                          = 0
read(0, "", 1)                          = 0
read(0, "", 1)                          = 0
read(0, "", 1)                          = 0
read(0, "", 1)                          = 0
read(0, "", 1)                          = 0
................ad nauseum

mientras que el servicio aún registra eventos y ejecuta los comandos condicionales cuando ocurren eventos reales. ¿Qué salió mal aquí?

ps esto sucede con cras_monitorpero no con acpi_listen. Intenté asegurarme de que el whilebucle solo se iniciara después de asegurarme de que el servicio subyacente se iniciara correctamente, pero fue en vano.

event_monitorActualización: aquí hay algunos fragmentos del código potencialmente relevantes :

...
#include <headers.h>
...
# Print to console function:
static void event_occurrence(void *context, int32_t attribute)
{
    printf("Some attribute has changed to %d.\n", attribute);
}
...
int main(int argc, char **argv)
{
    struct some_service_client *client # defined in headers
    int rc
...
# Some routine
...
    some_service_client_set_event_occurence_callback(client,event_occurence)
...
    rc = some_func(client)
...
    while (1) {
        int rc;
        char c;
        rc = read(STDIN_FILENO, &c, 1);
        if (rc < 0 || c == 'q')
            return 0;
    }
...
}


Respuesta1

Es su event_monitorprograma el que está en bucle, consumiendo toda la CPU, no su script bash.

Cuando se ejecuta bajo systemd, STDIN tiene /dev/null adjunto (o tal vez incluso esté cerrado). Cuando el bucle del monitor de eventos en main hace un read(2), obtiene EOF y recorre el bucle nuevamente.

Cuando se ejecuta de forma interactiva, event_monitor tiene el terminal conectado a la entrada estándar, por lo que read(2)se bloquea hasta que hay entrada.

event_monitor solo debería realizar un bucle al leer la entrada estándar si está abierto. Si recibe EOF, debería salir (probablemente no sea deseable en este caso) o simplemente dormir durante un largo tiempo.

Si no puede cambiar event_monitor, es posible que tenga éxito al conectar una FIFO (canalización con nombre) a la entrada estándar del servicio. systemd tiene la StandardInputopción (documentada en la página del manual systemd.exec(5)), donde puede especificar StandardInput=file:/run/event_monitor_ctl. Entonces sólo necesitas crear la /run/event_monitor_ctltubería con nombre. Para eso, puede usar systemd-tmpfiles creando un archivo de configuración (consulte tmpfiles.d(5)) para crear esa canalización con nombre.

información relacionada