我编写了一个脚本来根据事件监视器通知的输入事件运行特定操作,它看起来像
$ 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
显示在处理器允许的范围内没有执行任何操作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
程序在循环,耗尽了所有的 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 来创建该命名管道。