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프로세서가 허용하는 한 어떤 작업도 수행되지 않았음을 보여줍니다 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_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) 참조).

관련 정보