![bash while 루프에 "read"가 포함된 스크립트는 systemd 서비스로 실행될 때 높은 CPU 사용량을 유발할 수 있습니다.](https://linux55.com/image/192284/bash%20while%20%EB%A3%A8%ED%94%84%EC%97%90%20%22read%22%EA%B0%80%20%ED%8F%AC%ED%95%A8%EB%90%9C%20%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EB%8A%94%20systemd%20%EC%84%9C%EB%B9%84%EC%8A%A4%EB%A1%9C%20%EC%8B%A4%ED%96%89%EB%90%A0%20%EB%95%8C%20%EB%86%92%EC%9D%80%20CPU%20%EC%82%AC%EC%9A%A9%EB%9F%89%EC%9D%84%20%EC%9C%A0%EB%B0%9C%ED%95%A0%20%EC%88%98%20%EC%9E%88%EC%8A%B5%EB%8B%88%EB%8B%A4..png)
이벤트 모니터에서 알림을 받은 입력 이벤트를 기반으로 특정 작업을 실행하는 스크립트를 작성했는데 다음과 같습니다.
$ 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
bash 스크립트가 아니라 반복하여 모든 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) 참조).