作为 systemd 服务运行时,bash while 循环中包含“read”的脚本会导致 CPU 使用率过高

作为 systemd 服务运行时,bash while 循环中包含“read”的脚本会导致 CPU 使用率过高

我编写了一个脚本来根据事件监视器通知的输入事件运行特定操作,它看起来像

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

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

当从终端作为脚本运行时bash,脚本会消耗正常数量的 CPU,并且仅在打印新行时才执行操作。但是,当使用以下设置作为服务运行时

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

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

[Install]
WantedBy=default.target

event_monitor命令现在接管整个逻辑核心,并strace显示在处理器允许的范围内没有执行任何操作readread()

$ 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程序在循环,耗尽了所有的 CPU,而不是你的 bash 脚本。

当在 systemd 下运行时,STDIN 附加了 /dev/null(或者甚至可能是关闭的)。当 main 中的事件监视器循环执行 a 时read(2),它会获得 EOF,并再次循环。

当以交互方式运行时,event_monitor 的终端连接到 stdin,因此read(2)会阻塞,直到有输入。

event_monitor 应该仅在打开的情况下循环读取标准输入。如果它收到 EOF,它应该退出(在这种情况下可能不合需要),或者只是休眠很长时间。

如果您无法更改event_monitor,则可能会成功地将 FIFO(命名管道)附加到服务的 stdin。 systemd 有一个StandardInput选项(记录在 systemd.exec(5) 手册页中),您可以在其中指定StandardInput=file:/run/event_monitor_ctl.然后你只需要创建/run/event_monitor_ctl命名管道。为此,您可以通过创建配置文件(请参阅 tmpfiles.d(5))来使用 systemd-tmpfiles 来创建该命名管道。

相关内容