bash whileループに「read」を含むスクリプトは、systemdサービスとして実行すると高いCPU使用率を引き起こす可能性があります。

bash whileループに「read」を含むスクリプトは、systemdサービスとして実行すると高い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_listenwhile基本サービスが正常に開始されたことを確認した後にのみループが開始されることを確認しようとしましたが、役に立ちません。

更新:以下は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_monitorbashスクリプトではなく、すべてのCPUを繰り返し使用するのはプログラムです。

systemdで実行すると、STDINは/ dev / nullに追加されます(または閉じることもあります)。メインのイベントモニターを経由してループすると、read(2)EOFが取得され、再ループされます。

対話的に実行すると、event_monitorの端末はstdinに接続され、read(2)入力があるまでブロックされます。

event_monitorは、開いている場合は標準入力を読み取るために繰り返す必要があります。 EOFを受け取ると、終了するか(この場合は望ましくない可能性があります)、長時間スリープ状態にする必要があります。

変更できない場合は、event_monitorサービスのstdinに名前付きパイプ(FIFO)を正常に接続できます。 systemdには、StandardInputこれを指定するオプション(systemd.exec(5)のマニュアルページに記載されている)があります。その後、名前付きパイプをStandardInput=file:/run/event_monitor_ctl作成するだけです。/run/event_monitor_ctlこれを行うには、systemd-tmpfilesを使用して構成ファイルを生成して名前付きパイプを生成できます(tmpfiles.d(5)を参照)。

関連情報