명명된 파이프에서 일정한 크기의 버퍼를 읽고 각 버퍼에 대해 명령을 실행하는 방법

명명된 파이프에서 일정한 크기의 버퍼를 읽고 각 버퍼에 대해 명령을 실행하는 방법

터미널에는 다음과 같은 기능이 있습니다 redis-cli -x PUBLISH myChannel. 이 장치에서 데이터를 읽고 stdin작업을 수행합니다.

FIFO 자체는 청크 데이터를 제공하지 않습니다. 따라서 스트림에서 청크를 생성하고 redis-cli -x PUBLISH myChannel각 청크에 대해 실행해야 합니다.

나는 이것을 시도했습니다 : { while :; do dd iflag=fullblock iflag=nonblock bs=65536 count=1 2> /dev/null | redis-cli -x PUBLISH myChannel ; done } < myFifo

작동 중입니다. 하지만 CPU 소모량이 높습니다. 그리고 이는 확장에 매우 좋지 않습니다(50개의 동시 명령으로 인해 40%의 CPU 사용량이 발생함).

@terdon이 제공한 솔루션도 사용해 보았습니다.[여기]:

{ 
  while :; do 
    dd iflag=fullblock iflag=nonblock bs=65536 count=1 2> /dev/null | 
      redis-cli -x PUBLISH myChannel 
    sleep 0.1
  done 
} < myFifo

더 적은 CPU를 사용합니다(동시 명령 50개에 대해 20%). 그러나 여전히 더 큰 규모의 문제가 발생할 수 있습니다.

나는 또한 다음을 사용하여 tail -FFiFo에서 읽기를 시도했습니다.@raffa가 제안한대로:

tail -c +1 -F myFifo | 
  dd iflag=fullblock bs=65536 2> /dev/null | 
    redis-cli -x PUBLISH myChannel

redis-cli -x PUBLISH myChannel불행히도 이것은 모든 블록에 대해 실행되어야 하기 때문에 작동하지 않습니다 . 데이터 스트림을 수신할 수 없습니다.

이 명령의 기능을 수행하면서 CPU 효율성을 높일 수 있는 방법이 있습니까? 솔루션이 다음 단계에 있어야 한다고 덧붙일 수도 있습니다.우분투 16.04 이상.

매우 감사합니다.

답변1

나는 이것을 시도했습니다 :{ while :; do dd iflag=fullblock iflag=nonblock bs=65536 count=1 2> /dev/null | redis-cli -x PUBLISH myChannel ; done } < myFifo

작동 중입니다. 하지만 CPU 소모량이 높습니다. 그리고 이는 확장에 매우 좋지 않습니다(50개의 동시 명령으로 인해 40%의 CPU 사용량이 발생함).

iflag=nonblockGNU coreutils dd는 비차단 읽기를 사용해야 합니다. 즉, 읽을 데이터가 없으면 운영 체제 핸들을 떠나 더 많은 데이터를 기다리지 않고 오류 코드 EAGAIN 또는 EWOULDBLOCK과 함께 제어가 즉시 프로세스에 반환됩니다. dd가 이것을 다른 오류와 마찬가지로 처리하여 오류 메시지를 인쇄하고 종료하는 것처럼 보입니다. 루프가 dd를 다시 시작하기 때문에 동일한 상황이 계속해서 빠르게 연속적으로 발생하게 됩니다. 물론 이로 인해 CPU 사용량이 높아집니다.

오류를 로 리디렉션하지 않으면 /dev/null아래와 같이 dd 출력이 표시되며, 아무것도 하지 않고 짧은 시간 동안 반복적으로 실행되는 방식을 명확하게 보여줍니다.

dd: error reading 'standard input': Resource temporarily unavailable
0+0 records in
0+0 records out
0 bytes copied, 4.3734e-05 s, 0.0 kB/s
[...]

제안:

1) 무언가를 테스트하거나 최선의 방법을 시도하는 경우아니요오류를 숨기면 매우 유용합니다.

2) 데이터를 읽는 중이고 다른 할 일이 없으면 시스템 호출을 차단합니다.

제거는 iflag=nonblock이 문제를 해결하는 데 큰 도움이 됩니다.

또한 dd가 필요한지 잘 모르겠습니다. head -c 65536비슷한 작업을 수행해야 합니다.


하지만 읽고 있는 파이프가 닫혀 있을 때 무슨 일이 일어나는지는 여전히 열려 있습니다. 이 시점에서 모든 읽기는 (성공적으로) 0바이트를 반환하여 데이터 스트림의 끝을 나타냅니다. 이 시점에서 dd는 0바이트를 읽은 채 종료되고 루프는 계속해서 다시 시작합니다. 안타깝게도 이를 감지하는 것이 더 어렵습니다. dd에 관한 한 여전히 성공적인 실행이기 때문입니다.

데이터를 전달하기 전에 임시 파일로 읽어오도록 선택할 수 있으므로 계속 진행하기로 결정하기 전에 파일 크기를 확인하거나 dd의 stderr 출력을 읽어 복사된 데이터의 양을 확인할 수 있습니다. (또는 데이터를 쉘 변수로 읽지만 대부분의 쉘에서는 8비트 깨끗하지 않습니다. zsh는 예외입니다.)

일반적으로 오류 발생 시 루프를 중단할 수 있도록 dd의 종료 상태도 확인해야 하지만 0바이트 실행도 확인해야 합니다.

또는 읽기-쓰기 모드에서 FIFO를 열 수 있으므로 실제 작성기가 닫히 dd거나 head루프에서 자체적으로 차단되는 경우가 있습니다. 루프는 종료되지 않지만 적어도 바쁜 루프에 빠지지는 않습니다.

아마도 다음과 같을 겁니다:

while true; do head -c 65536| redis ... ; done 0<> fifo

head또는 실패하거나 실패하면 루프를 중단합니다 redis.

set -o pipefail
while head -c 65536 | redis ... ; do true; done 0<> fifo

어쨌든, 이와 같은 것은 Perl과 같은 쉘이 아닌 다른 것에서 또는 일부 기존 도구를 사용하여 더 잘 구현될 수 있습니다. (좋다 split).

관련 정보