GN⁺: 파이프라인이 가끔 "멈추는" 이유: 버퍼링 문제
(jvns.ca)파이프가 "멈추는" 이유: 버퍼링
-
문제 설명: 로그 파일에서 특정 출력을 찾기 위해
tail -f /some/log/file | grep thing1 | grep thing2
명령어를 실행할 때, 로그 라인이 느리게 추가되면 출력이 나타나지 않는 문제 발생. 이는 파이프가 멈추는 것처럼 보이지만 실제로는 프로그램이 데이터를 파이프에 쓰지 않기 때문임.
버퍼링의 원인
- 버퍼링의 이유: 프로그램이 데이터를 파이프나 파일에 쓰기 전에 버퍼링을 하는 것이 일반적임. 이는 성능 향상을 위해 모든 출력을 즉시 쓰는 대신, 일정량의 데이터를 모아서 한 번에 쓰기 때문임.
-
예시:
grep thing1
은 8KB의 데이터를 모을 때까지 매칭된 데이터를 저장하고, 이로 인해 출력이 나타나지 않을 수 있음.
터미널에 쓸 때는 버퍼링하지 않음
-
터미널과 파이프의 차이:
grep
은 출력이 터미널로 향할 때는 라인 버퍼링을 사용하지만, 파이프로 향할 때는 블록 버퍼링을 사용함. 이는isatty
함수를 통해 결정됨.
버퍼링하는 명령어와 하지 않는 명령어
-
버퍼링하지 않는 명령어:
tail
,cat
,tee
등은 버퍼링하지 않음. -
버퍼링하는 명령어:
grep
,sed
,awk
,tcpdump
,jq
,tr
,cut
등은 버퍼링하며, 일부는 특정 플래그로 버퍼링을 비활성화할 수 있음.
프로그래밍 언어의 기본 출력 버퍼링
- 버퍼링하는 언어: C, Python, Ruby, Perl 등은 기본적으로 출력 버퍼링을 하며, 특정 방법으로 비활성화 가능함.
Ctrl-C
를 눌렀을 때 버퍼 내용 손실
-
문제 설명:
Ctrl-C
를 누르면 버퍼에 있는 내용이 손실됨. 이는SIGINT
신호가 먼저 전달되기 때문임. -
해결책:
tcpdump
의 PID를 찾아kill -TERM $PID
를 실행하면 버퍼를 플러시할 수 있음.
파일로 리다이렉션할 때도 버퍼링
-
파일 리다이렉션: 파일로 리다이렉션할 때도 버퍼링이 발생하지만,
Ctrl-C
로 인한 버퍼 손실 문제는 발생하지 않음.
버퍼링을 피하는 여러 방법
- 해결책 1: 빠르게 종료되는 프로그램 실행.
-
해결책 2:
grep
의--line-buffered
플래그 사용. -
해결책 3:
awk
사용. -
해결책 4:
stdbuf
사용. -
해결책 5:
unbuffer
사용.
버퍼링을 비활성화하는 환경 변수
-
아이디어:
PYTHON_UNBUFFERED
와 같은 표준 환경 변수가 있으면 좋겠다는 의견.NO_BUFFER
와 같은 변수를 제안함.
생략된 내용
- 생략된 주제: 라인 버퍼링과 완전한 비버퍼링의 차이, stderr와 stdout의 버퍼링 차이, 운영 체제의 TTY 드라이버 버퍼링 등.
Hacker News 의견
-
버퍼링된 접근은 일정 바이트 수나 시간이 지나면 플러시해야 함. 하드웨어 인터페이스에서 유사한 문제를 해결하는 일반적인 방법임
- 사용자 공간에서 버퍼링하는 라이브러리는 데이터를 처음 버퍼링할 때 적절한 타이머를 설정해야 함
- 타임아웃 파라미터는 인수로 전달하거나, 인간의 시간 척도보다 약간 낮거나, 대역폭/임계값에 비례하거나, 플러싱 오버헤드에 비례하는 것이 좋음
- 쓰기와 읽기 모두에 적용되며, 데이터 채널에 따라 다를 수 있음
-
시스템 전체 CPU가 유휴 상태가 되면 모든 버퍼를 플러시하고 싶음
- 버퍼링은 일반적으로 CPU 절약 기법임
- CPU가 유휴 상태가 되면 모든 프로세스에 "버퍼를 플러시하라"는 신호를 보내야 함
-
NIX 시스템을 20년 이상 다뤘지만, 버퍼링 문제를 항상 잊어버림
-
Unix를 35년 이상 사용했지만 버퍼링 작동 방식을 완전히 이해하지 못했음. 이 설명이 유익했음
-
"비버퍼링"과 "라인 버퍼링"을 혼동하고 있음
- 비버퍼링은 성능을 저하시킬 수 있으며, 여러 소스가 동일한 파이프에 쓰는 경우 잘못된 출력을 생성할 수 있음
- 라인 버퍼링은 터미널의 기본값이며, 파이프에 적합함
-
버퍼는 화면에 출력을 인쇄하는 것보다 버퍼에 쓰는 것이 상대적으로 매우 느리기 때문에 존재함
- UART 작업 시 자주 발생하는 문제이며, 다양한 해결책이 있음
- 특수 문자 사용, 길이 기반 접근, 시간 기반 접근 등 다양한 방법이 있음
-
Ctrl-C를 누르면 버퍼 내용이 손실될 수 있음
- 대부분의 프로그램은 SIGINT에서 버퍼를 플러시할 것이라고 생각함
-
Unix에서 버퍼링 문제를 겪었으며, 모든 'awk' 구현이 동일하게 작동하지 않음
-
얼어붙은 파이프 농담을 놓친 기분임