# 파이프라인이 가끔 "멈추는" 이유: 버퍼링 문제

> Clean Markdown view of GeekNews topic #18027. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=18027](https://news.hada.io/topic?id=18027)
- GeekNews Markdown: [https://news.hada.io/topic/18027.md](https://news.hada.io/topic/18027.md)
- Type: GN+
- Author: [neo](https://news.hada.io/@neo)
- Published: 2024-11-30T09:53:57+09:00
- Updated: 2024-11-30T09:53:57+09:00
- Original source: [jvns.ca](https://jvns.ca/blog/2024/11/29/why-pipes-get-stuck-buffering/)
- Points: 2
- Comments: 1

## Topic Body

##### 파이프가 "멈추는" 이유: 버퍼링

- **문제 설명**: 로그 파일에서 특정 출력을 찾기 위해 `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 드라이버 버퍼링 등.

## Comments



### Comment 31863

- Author: neo
- Created: 2024-11-30T09:53:57+09:00
- Points: 1

###### [Hacker News 의견](https://news.ycombinator.com/item?id=42275033) 
- 버퍼링된 접근은 일정 바이트 수나 시간이 지나면 플러시해야 함. 하드웨어 인터페이스에서 유사한 문제를 해결하는 일반적인 방법임
  - 사용자 공간에서 버퍼링하는 라이브러리는 데이터를 처음 버퍼링할 때 적절한 타이머를 설정해야 함
  - 타임아웃 파라미터는 인수로 전달하거나, 인간의 시간 척도보다 약간 낮거나, 대역폭/임계값에 비례하거나, 플러싱 오버헤드에 비례하는 것이 좋음
  - 쓰기와 읽기 모두에 적용되며, 데이터 채널에 따라 다를 수 있음

- 시스템 전체 CPU가 유휴 상태가 되면 모든 버퍼를 플러시하고 싶음
  - 버퍼링은 일반적으로 CPU 절약 기법임
  - CPU가 유휴 상태가 되면 모든 프로세스에 "버퍼를 플러시하라"는 신호를 보내야 함

- NIX 시스템을 20년 이상 다뤘지만, 버퍼링 문제를 항상 잊어버림

- Unix를 35년 이상 사용했지만 버퍼링 작동 방식을 완전히 이해하지 못했음. 이 설명이 유익했음

- "비버퍼링"과 "라인 버퍼링"을 혼동하고 있음
  - 비버퍼링은 성능을 저하시킬 수 있으며, 여러 소스가 동일한 파이프에 쓰는 경우 잘못된 출력을 생성할 수 있음
  - 라인 버퍼링은 터미널의 기본값이며, 파이프에 적합함

- 버퍼는 화면에 출력을 인쇄하는 것보다 버퍼에 쓰는 것이 상대적으로 매우 느리기 때문에 존재함
  - UART 작업 시 자주 발생하는 문제이며, 다양한 해결책이 있음
  - 특수 문자 사용, 길이 기반 접근, 시간 기반 접근 등 다양한 방법이 있음

- Ctrl-C를 누르면 버퍼 내용이 손실될 수 있음
  - 대부분의 프로그램은 SIGINT에서 버퍼를 플러시할 것이라고 생각함

- Unix에서 버퍼링 문제를 겪었으며, 모든 'awk' 구현이 동일하게 작동하지 않음

- 얼어붙은 파이프 농담을 놓친 기분임
