
Eu escrevi um script para executar ações específicas condicionadas a eventos de entrada informados por um monitor de eventos, que se parece com
$ cat script.sh
-----------------------------------------------------------
#!/usr/bin/bash
stdbuf -oL /usr/bin/event_monitor | while IFS= read LINE
do
something with $LINE
done
Quando executado como um script a partir de um bash
terminal, o script consome uma quantidade normal de CPU e executa a ação somente quando uma nova linha é impressa. No entanto, quando executado como um serviço com a seguinte configuração
$ cat event.service
-----------------------------------------------------------
[Unit]
Description=Actions upon events
[Service]
Type=simple
ExecStart=/path/to/script.sh
[Install]
WantedBy=default.target
O event_monitor
comando agora assume todo um núcleo lógico e strace
revela que não read
está read()
executando nada com a frequência que o processador permite:
$ 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
enquanto o serviço ainda registra eventos e executa comandos condicionais quando eventos reais ocorrem. O que deu errado aqui?
ps isso acontece com cras_monitor
mas não com acpi_listen
. Tentei garantir que o while
loop só iniciasse depois de garantir que o serviço subjacente fosse iniciado com êxito, mas sem sucesso.
Atualização: aqui estão alguns trechos potencialmente relevantes do event_monitor
código de:
...
#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;
}
...
}
Responder1
É o seu event_monitor
programa que está em loop, usando toda a CPU, não o seu script bash.
Quando executado no systemd, STDIN tem /dev/null anexado (ou talvez até esteja fechado). Quando o loop do monitor de eventos em main executa um read(2)
, ele obtém EOF e percorre o loop novamente.
Quando executado de forma interativa, event_monitor tem o terminal anexado ao stdin, então ele read(2)
bloqueia até que haja entrada.
event_monitor só deve fazer um loop na leitura de stdin se estiver aberto. Se receber EOF, deverá sair (provavelmente não desejável neste caso) ou apenas dormir por um longo tempo.
Se você não conseguir alterar event_monitor
, poderá ter algum sucesso ao anexar um FIFO (pipe nomeado) ao stdin do serviço. systemd tem a StandardInput
opção (documentada na página man systemd.exec(5)), onde você pode especificar StandardInput=file:/run/event_monitor_ctl
. Então você só precisa criar o /run/event_monitor_ctl
pipe nomeado. Para isso, você pode usar systemd-tmpfiles criando um arquivo de configuração (veja tmpfiles.d(5)) para criar esse pipe nomeado.