скрипт с `read` в цикле bash while, вызывающий высокую загрузку ЦП при запуске в качестве службы systemd

скрипт с `read` в цикле bash while, вызывающий высокую загрузку ЦП при запуске в качестве службы systemd

Я написал скрипт для запуска определенных действий в зависимости от событий ввода, о которых сообщает монитор событий. Он выглядит примерно так:

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

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

При запуске в качестве скрипта из bashтерминала скрипт потребляет обычное количество ресурсов ЦП и выполняет действие только при печати новой строки. Однако при запуске в качестве службы со следующей настройкой

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

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

[Install]
WantedBy=default.target

Теперь команда event_monitorзахватывает все логическое ядро ​​и straceпоказывает, что ничего не readделает read()так часто, как позволяет процессор:

$ 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

в то время как служба все еще регистрирует события и выполняет условные команды, когда происходят реальные события. Что здесь пошло не так?

ps это происходит с , cras_monitorно не с acpi_listen. Я пытался убедиться, что whileцикл запускается только после того, как базовая служба успешно запустится, но безуспешно.

Обновление: вот некоторые потенциально важные фрагменты event_monitorкода:

...
#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;
    }
...
}


решение1

Это ваша event_monitorпрограмма зацикливается, используя все ресурсы процессора, а не ваш bash-скрипт.

При запуске под systemd STDIN имеет присоединенный /dev/null (или, возможно, даже закрыт). Когда цикл монитора событий в main делает a read(2), он получает EOF и снова проходит по циклу.

При интерактивном запуске event_monitor подключает терминал к stdin, поэтому read(2)блокируется до тех пор, пока не поступит ввод.

event_monitor должен зацикливаться только на чтении stdin, если он открыт. Если он получает EOF, он должен либо выйти (вероятно, нежелательно в этом случае), либо просто спать долгое время.

Если вы не можете изменить event_monitor, вы можете добиться некоторого успеха, прикрепив FIFO (именованный канал) к stdin службы. systemd имеет опцию StandardInput(задокументированную на странице man systemd.exec(5)), где вы можете указать StandardInput=file:/run/event_monitor_ctl. Затем вам просто нужно создать /run/event_monitor_ctlименованный канал. Для этого вы можете использовать systemd-tmpfiles, создав файл конфигурации (см. tmpfiles.d(5)) для создания этого именованного канала.

Связанный контент