이라는 레시피가 포함된 makefile이 있고 hour_long_recipe
이름에서 알 수 있듯이 실행하는 데 1시간이 걸린다고 가정해 보겠습니다. 레시피 전반에 걸쳐 무작위로 예/아니요 질문을 합니다. 총 10개의 질문을 한다고 가정해 보겠습니다.
이를 실행하는 한 가지 가능한(그리고 종종 권장되는) 방법은 다음과 같습니다.
yes | make hour_long_recipe
그것은 모든 질문에 답합니다 y
. 그러나 내 이해에 따르면 yes
출력은 지속적으로 표준 출력으로 출력됩니다.초당 최대 10.2GiBmake
표준 입력의 데이터가 실제로 사용되는지 여부에 관계없이 .
10MiB/s( yes
Reddit 스레드 구현보다 훨씬 느림)에 불과하더라도 한 시간 안에 총 35GiB가 넘으며 그 중 20바이트만 읽혀집니다. 데이터는 어디로 가나요? 디스크에 저장하는 것은 가능하지만 이는 낭비이며 디스크가 빨리 가득 차면 make
장애를 일으킬 수도 있습니다.
아마도 운영 체제가 이를 방지하는 것 같지만, 어떻게 해야 할까요? 제한 사항은 무엇입니까? 해당 한도에 도달한 후에는 어떻게 되나요?
답변1
tl;dr: 어느 시점에서 yes
상대방이 데이터를 읽지 않으면 쓰기가 차단됩니다. 데이터를 읽거나 신호를 수신할 때까지 실행을 계속할 수 없으므로 일반적으로 yes
기가바이트의 데이터 쓰기에 대해 걱정할 필요가 없습니다.
기억해야 할 중요한 점은관로순수 스트림이 아닌 FIFO 데이터 구조로, 수신기에서 즉시 읽히지 않으면 데이터를 삭제합니다. 즉, 대부분의 경우 쓰기 애플리케이션에서 읽기 애플리케이션으로의 원활한 데이터 흐름으로 보일 수 있지만 이를 위해서는 중간 저장소가 필요하며 중간 저장소의 크기는 제한되어 있습니다. *
우리가 보면Pipe(7) 매뉴얼 페이지, 이 내부 버퍼의 크기에 대한 다음 정보를 읽을 수 있습니다(강조 추가).
2.6.11 이전의 Linux 버전에서는 파이프 용량이 시스템 페이지 크기(예: i386의 4096바이트)와 동일했습니다. 리눅스 2.6.11부터,파이프 용량은 16페이지입니다.(즉, 페이지 크기가 4096바이트인 시스템에서는 65,536바이트입니다.) Linux 2.6.35부터 기본 파이프 용량은 16페이지이지만 fcntl(2) F_GETPIPE_SZ 및 F_SETPIPE_SZ 작업을 사용하여 용량을 쿼리하고 설정할 수 있습니다.
표준 x86_64 시스템을 사용한다고 가정하면 4KiB 페이지를 사용할 가능성이 가장 높으므로 파이프의 어느 한 쪽이 어느 시점에서 사용되지 않는 한 파이프 용량의 상한은 2^16이 아마도 정확할 것입니다 fcntl(F_SETPIPE_SZ)
. 어느 쪽이든 원칙은 유지됩니다. 파이프 양쪽 사이의 중간 저장 공간은 제한되어 메모리에 저장됩니다.
추상 파이프라인에서 이 저장소는 a | b
일부 데이터를 쓰는 것과 실제로 데이터를 읽는 사이의 기간 동안 사용됩니다. 글쎄, 여러분의 호출(및 상속을 통해 이 파이프에 연결된 모든 하위 항목)이 실제로 표준 입력을 읽으려고 시도하지 않거나 거의 그렇게 하지 않는다고 가정하면 버퍼 공간이 고갈되면 시스템 호출은 결국 깨어나지 않을 것입니다. 잠에서. 그런 다음 버퍼 공간을 다시 사용할 수 있거나 신호가 수신될 때 깨어나기를 기다립니다. **이 모든 것은 커널의 프로세스 스케줄러에 의해 처리됩니다. 당신은 할 수a
b
make
write
yes
yes
yes
pipe_write()
, 이는 write()
파이프의 핸들러입니다.
static ssize_t
pipe_write(struct kiocb *iocb, struct iov_iter *from)
{
/* ... */
if (pipe_full(pipe->head, pipe->tail, pipe->max_usage))
wake_next_writer = false;
if (wake_next_writer)
wake_up_interruptible_sync_poll(&pipe->wr_wait, EPOLLOUT | EPOLLWRNORM);
/* ... */
}
해당 make
끝이 결국 종료되면 파이프에 쓴 결과로 전송되며 yes
다른 쪽 끝에는 아무것도 남지 않습니다. SIGPIPE
그런 다음 yes
구현에 따라 자체 신호 처리기 또는 기본 커널 신호 처리기를 호출한 다음 종료됩니다. ***
* 수신자가 데이터를 쓰는 것과 거의 같은 속도로 데이터를 처리하는 간단한 경우, 이 전송은 가상 메모리 맵을 사용하고 물리적 페이지를 제공함으로써 중간 버퍼 없이 제로 복사가 될 수도 있습니다. 받는 사람. 그러나 설명하는 상황에서는 결국 읽지 않은 데이터를 저장하기 위해 파이프 버퍼를 사용해야 합니다.
O_NONBLOCK
** 쓰기는 비차단 모드를 활성화하는 파일 설명자에 설정된 플래그를 통해 수행 될 수도 있습니다. 이 경우 불완전한 쓰기가 발생할 수 있으며 쓰기가 반환되고 EAGAIN
애플리케이션은 이를 자체적으로 처리해야 합니다. 파이프가 가득 찼다는 사실을 처리하기 위해 선택한 다른 코드를 일시 중지하거나 실행하여 이를 수행할 수 있습니다. 그러나 내가 찾을 수 있는 모든 최신 버전과 대부분의 다른 앱 에서는 yes
.O_NONBLOCK
*** 애플리케이션은 애플리케이션을 받은 후에 무엇이든 할 수 있습니다 SIGPIPE
. 이론적으로는 종료하지 않기로 결정할 수도 있습니다. 그러나 모든 일반적인 핸들러는 더 이상 사용자 공간 명령을 실행하지 않고 단순히 종료되는 yes
기본 핸들러를 사용합니다 .SIGPIPE