Richard Stevens가 쓴 "Unix 네트워크 프로그래밍"이라는 책을 공부하는 동안 클라이언트와 서버 간의 FIFO 사용에 대해 설명하는 다음 줄을 발견했습니다.
클라이언트 프로세스가 시작되고 쓰기를 위해 FIFO를 열고 요청을 작성한 다음 종료됩니다. 클라이언트 프로세스가 종료될 때마다 읽기 작업이 데몬에 0을 반환하게 됩니다. 그런 다음 데몬은 FIFO를 다시 열고(읽기 전용) 클라이언트 프로세스가 쓰기 위해 FIFO를 열 때까지 기다려야 합니다.
마지막줄이 이해가 안가네요. 서버 프로세스가 FIFO를 다시 열어야 하는 이유는 무엇입니까? 클라이언트 프로세스가 쓴 후에 다시 읽으면 됩니다. 그렇죠?
답변1
서버 프로세스가 FIFO를 다시 열어야 하는 이유는 무엇입니까? 클라이언트 프로세스가 쓴 후에 다시 읽으면 됩니다. 그렇죠?
흥미롭군요. 귀하의 제안을 시도해 보겠습니다. 다음 결과는 Linux 4.9.0-6-amd64(Ubuntu Linux 커널)에서 생성되었습니다.
$ mkfifo t
$ (cat; cat) < t & # run a "server" as a background job
[1] 4856
$ echo 1 > t
1
[1]+ Done ( cat; cat ) < t
우리가 원하는 대로 작동하지 않았습니다. 첫 번째는 cat
예상대로 EOF를 읽은 다음 종료됩니다. 문제는 두번째인데cat
반품즉시 EOF를 읽으면 "서버"가 완료됩니다. 그건 말도 안 돼기다리다새로운 클라이언트의 경우(read()를 반복적으로 호출하여 CPU 시간을 낭비할 필요가 없습니다).
셸에서 파일 설명자(FD)를 조작하는 방법을 알고 있다면 확인하는 데 도움이 될 수 있는 또 다른 방법이 있습니다.
$ echo 1 > t &
$ exec 3 < t # open "t" for reading, as FD 3.
$ cat <&3
1
[1]+ Done echo 1 > t
$ cat <&3
$
$ echo 2 > t &
[1] 5102
$ cat <&3
2
[1]+ Done echo 2 > t
$ cat <&3
$
대답은 open()
FIFO를 재설정하는 목적은 다른 사람이 쓰기 위해 FIFO를 여는 것을 방지하는 것입니다. 이 단계가 없으면 read()
fifo에 대한 모든 후속 호출은 즉시 0(EOF)을 반환합니다.
이것을 발견했을 때 나는 systemd-initctl
그것이 어떻게 작동하는지 궁금했습니다. 이 프로그램은 systemd 아래의 이전 fifo를 에뮬레이트합니다 /dev/initctl
. (면책 조항: 테스트하는 것은 쉽지 않습니다. 수행 방법을 문서화하지 않겠습니다.) 대답은 systemd-initctl이 읽기 및 쓰기를 위해 FIFO를 여는 것입니다. (기술적으로 systemd는 systemd-initctl.socket에 지정된 대로 fifo를 열고 이를 systemd-initctl에 전달합니다.) 동시 읽기 및 쓰기를 위해 fifo를 여는 것은 Linux 전용 기능입니다. 그러나 이렇게 함으로써 systemd는 Stevens가 다음에 언급한 것과 동일한 트릭을 구현합니다.
이를 방지하기 위한 유용한 기술은 데몬이 FIFO를 두 번(읽기용으로 한 번, 쓰기용으로 한 번) 여는 것입니다. 읽기를 위해 반환된 파일 설명자는 읽기 클라이언트 요청에 사용되는 반면, 쓰기에 사용된 파일 설명자는 절대 사용되지 않습니다. 쓰기를 위해 FIFO를 항상 열어두면(데몬이 존재하는 한) 읽기는 EOF를 반환하지 않고 다음 클라이언트 요청을 기다립니다.