bash while 루프에서 '읽기'가 포함된 스크립트는 systemd 서비스로 실행될 때 높은 CPU 사용량을 발생시킵니다.

bash while 루프에서 '읽기'가 포함된 스크립트는 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_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는 열려 있는 경우 stdin을 읽을 때만 반복해야 합니다. EOF를 받으면 종료되거나(이 경우에는 바람직하지 않음) 오랫동안 잠자기 상태로 있어야 합니다.

을 변경할 수 없는 경우 event_monitor서비스의 stdin에 FIFO(명명된 파이프)를 연결하면 어느 정도 성공할 수 있습니다. systemd StandardInput에는 StandardInput=file:/run/event_monitor_ctl. 그런 다음 명명된 파이프를 생성하기만 하면 됩니다 /run/event_monitor_ctl. 이를 위해 구성 파일(tmpfiles.d(5) 참조)을 생성하여 systemd-tmpfiles를 사용하여 명명된 파이프를 생성할 수 있습니다.

관련 정보