Linux의 "누수" 파이프

Linux의 "누수" 파이프

다음과 같은 파이프라인이 있다고 가정해 보겠습니다.

$ a | b

stdin 처리를 중지 하면 b잠시 후 파이프가 채워지고 astdout에서 쓰기가 차단됩니다( b처리를 다시 시작하거나 종료될 때까지).

이것을 피하고 싶다면 buffer(1)다음과 같이 더 큰 파이프(또는 더 간단히 )를 사용하고 싶을 수도 있습니다.

$ a | buffer | b

이것은 나에게 더 많은 시간을 벌어주지만 결국에는 a중지됩니다.

내가 원하는 것은(내가 해결하고 있는 매우 구체적인 시나리오에 대해) 파이프가 가득 차면 버퍼에서 일부 데이터를 제거하여(이상적으로는 한 줄씩) a처리를 계속할 수 있도록 하는 "누수" 파이프를 갖는 것입니다. 파이프라인을 통해 흐르는 데이터는 소모적입니다. 즉, 데이터 처리는 차단 없이 실행할 b수 있는 것보다 더 중요하지 않습니다 a.

대체로 나는 제한적이고 누출이 있는 버퍼와 같은 것을 갖고 싶습니다.

$ a | leakybuffer | b

아마도 어떤 언어로든 쉽게 구현할 수 있을 것입니다. 제가 놓친 "즉시 사용할 수 있는" 것(또는 bash one-liner 같은 것)이 있는지 궁금합니다.

참고: 이 예에서는 일반 파이프를 사용하고 있지만 질문은 명명된 파이프에도 적용됩니다.


아래에 답변을 주었지만 아래의 간단한 솔루션에는 몇 가지 제한 사항이 있기 때문에 Leakybuffer 명령도 구현하기로 결정했습니다.https://github.com/CAFxX/leakybuffer

답변1

가장 쉬운 방법은 비차단 출력을 설정하는 일부 프로그램을 통해 파이프하는 것입니다. 이것은 간단한 Perl oneliner입니다(다른 이름으로 저장할 수 있습니다).누출 버퍼)이 방법:

그래서 당신은 a | b다음과 같이 됩니다:

a | perl -MFcntl -e \
    'fcntl STDOUT,F_SETFL,O_NONBLOCK; while (<STDIN>) { print }' | b

이것이 하는 일은 입력을 읽고 출력을 쓰는 것입니다( 와 동일 cat(1)). 그러나 출력은 비차단입니다. 즉, 쓰기가 실패하면 오류를 반환하고 데이터가 손실되지만 프로세스는 다음 입력 줄에서 계속됩니다. 우리는 편리하게 오류를 무시합니다. 예상한 대로 프로세스는 라인 버퍼이지만 아래 경고를 참조하세요.

다음 명령을 사용하여 테스트할 수 있습니다.

seq 1 500000 | perl -w -MFcntl -e \
    'fcntl STDOUT,F_SETFL,O_NONBLOCK; while (<STDIN>) { print }' | \
    while read a; do echo $a; done > output

다음과 같이 누락된 줄이 있는 파일을 얻게 됩니다 output(정확한 출력은 셸 속도 등에 따라 달라집니다).

12768
12769
12770
12771
12772
12773
127775610
75611
75612
75613

쉘이 그 다음에 라인을 잃는 위치를 볼 수 있을 것입니다 12773. 그러나 그것은 또한 예외입니다 - Perl은 버퍼가 충분하지 않기 12774\n때문에 1277그냥 씁니다 - 그래서 다음 숫자는 75610라인의 처음부터 시작하지 않습니다. 꽤 추악하게 만듭니다.

이는 쓰기가 완전히 성공하지 못한 경우 Perl이 이를 감지하고 들어오는 새 줄을 무시하면서 나머지 줄을 플러시하도록 함으로써 개선될 수 있습니다. 그러나 이렇게 하면 Perl 스크립트가 더 복잡해지기 때문에 관심 있는 독자를 위한 연습으로 남겨둡니다. )

업데이트(바이너리용): 개행으로 끝나는 줄(예: 로그 파일 등)을 처리하지 않는 경우 명령을 약간 변경해야 합니다. 그렇지 않으면 Perl은 많은 메모리를 소비합니다(입력에 개행이 나타나는 빈도에 따라 다름).

perl -w -MFcntl -e 'fcntl STDOUT,F_SETFL,O_NONBLOCK; while (read STDIN, $_, 4096) { print }' 

또한 바이너리 파일에서도 작동합니다(추가 메모리가 소모되지 않음).

업데이트 2 - 더 나은 텍스트 파일 출력: 출력 버퍼를 피하세요( syswrite대신 print):

seq 1 500000 | perl -w -MFcntl -e \
    'fcntl STDOUT,F_SETFL,O_NONBLOCK; while (<STDIN>) { syswrite STDOUT,$_ }' | \
    while read a; do echo $a; done > output

나에게 "병합 라인" 문제를 해결하는 것 같습니다.

12766
12767
12768
16384
16385
16386

(참고: perl -ne '$c++; next if $c==$_; print "$c $_"; $c=$_' outputoneliner를 사용하여 잘린 출력 줄을 확인할 수 있습니다.)

관련 정보