Pipe

mongBrown·2026년 4월 16일

Pipe — 프로세스가 데이터를 주고받는 방식

터미널에서 이런 명령어를 자주 쓴다.

ps aux | grep java

ps aux는 현재 실행 중인 전체 프로세스 목록을 출력하고, grep java는 그 중 "java"가 포함된 줄만 걸러낸다. 이 둘은 완전히 별개의 프로세스다. 서로 메모리를 공유하지 않는 두 프로세스가 어떻게 데이터를 주고받는 걸까.


파이프의 실체는 무엇인가

파이프는 OS가 커널 안에 만들어주는 버퍼다. 두 프로세스가 직접 데이터를 주고받는 게 아니라, 커널 버퍼를 사이에 두고 한쪽은 쓰고 한쪽은 읽는 구조다.

ps aux  →  [커널 버퍼]  →  grep java

ps auxwrite() 시스템 콜로 커널 버퍼에 데이터를 밀어 넣는다. grep javaread() 시스템 콜로 그 버퍼에서 데이터를 꺼내 처리한다. 두 프로세스 사이에 직접적인 연결은 없고, OS가 만들어준 버퍼 하나가 중간 다리 역할을 한다.

그런데 데이터가 항상 왼쪽에서 오른쪽으로만 흘러간다. grep java가 반대로 ps aux에게 데이터를 보낼 수는 없다.

왜 단방향인가

파이프를 만들 때 OS는 파일 디스크립터를 두 개 만든다.

fd[0] → 읽기 전용 (read end)
fd[1] → 쓰기 전용 (write end)

ps aux는 fd[1]만 받는다. 쓰기 전용이니 읽을 수가 없다. grep java는 fd[0]만 받는다. 읽기 전용이니 쓸 수가 없다. grep java가 반대 방향으로 데이터를 보내려 해도, 가진 fd가 읽기 전용이라 쓸 수 있는 수단 자체가 없다. 양방향 통신이 필요하다면 파이프를 두 개 만들어 반대 방향으로 하나 더 연결해야 한다.

파이프 왼쪽엔 stdout으로 결과를 출력하는 명령어가, 오른쪽엔 stdin으로 데이터를 받아 처리하는 명령어가 온다. vi처럼 인터랙티브 편집기는 stdout으로 내용을 출력하지 않기 때문에 파이프 왼쪽에 올 수 없다.

버퍼가 꽉 차면 어떻게 되는가

파이프 버퍼에는 크기 한계가 있다. ps aux가 데이터를 빠르게 쏟아내는 속도를 grep java가 따라가지 못하면 버퍼가 꽉 찰 수 있다.

이때 데이터가 유실되지는 않는다. OS가 writer인 ps aux를 자동으로 멈추고 대기시킨다. 사용자 입장에서는 출력이 잠깐 지연되는 것처럼 느껴진다. grep java가 버퍼에서 데이터를 읽어 공간이 생기면, ps aux는 다시 쓰기를 재개한다.

반대로 버퍼가 비어있으면 reader인 grep java가 멈추고 기다린다. writer가 데이터를 다시 쓰기 시작하면 reader도 깨어난다. 별도로 락을 구현하거나 데이터가 왔는지 확인하는 로직 없이, OS가 이 흐름을 자동으로 관리한다.

지금까지 설명한 | 파이프는 쉘이 두 프로세스를 동시에 실행하면서 연결해주는 방식이다. 이미 실행 중인 프로세스끼리는 이 방식으로 연결할 수 없다.

실행 시점이 다른 프로세스끼리 연결하려면

|로 쓰는 파이프는 정식 명칭이 anonymous pipe다. 쉘이 두 프로세스를 동시에 띄우고, 한쪽의 stdout을 다른 쪽의 stdin에 연결해준다. 쉘이 중간에서 연결을 맺어주기 때문에, 두 프로세스는 쉘이 실행해준 관계여야만 한다.

이럴 때 쓰는 게 Named pipe다. mkfifo 명령으로 파일 시스템에 파이프를 파일처럼 만들어두고, 두 프로세스가 그 파일 경로를 통해 각자 연결한다.

# 1단계: 파이프 파일 생성
mkfifo /tmp/mypipe

# 2단계: 터미널 A에서 reader 실행 — 데이터가 올 때까지 대기
cat /tmp/mypipe

# 3단계: 터미널 B에서 writer 실행 — "hello"를 파이프에 씀
echo "hello" > /tmp/mypipe

터미널 A에서 대기하던 cat은 데이터를 받는 순간 화면에 "hello"를 출력하고 종료된다. anonymous pipe는 쉘이 writer와 reader를 동시에 실행하며 연결해주고, 명령이 끝나면 파이프도 사라진다. Named pipe는 파일로 남아있기 때문에 서로 다른 시점에 실행된 프로세스들이 같은 파일 경로를 통해 연결될 수 있다.

Named pipe는 어떻게 종료하는가

writer가 쓰기를 마치고 파이프를 닫으면 reader는 EOF 신호를 받고 자동으로 종료된다. 그런데 파이프 파일 자체는 자동으로 사라지지 않는다. anonymous pipe와 달리 named pipe는 파일 시스템에 남아있기 때문에, 사용이 끝나면 직접 삭제해야 한다.

rm /tmp/mypipe

파이프는 anonymous든 named든 같은 머신 안에서만 쓸 수 있다. 다른 서버와 통신해야 한다면 얘기가 달라진다.

파이프와 소켓은 언제 구분해서 쓰는가

소켓은 네트워크 너머 다른 머신과도 통신할 수 있다. HTTP 통신, TCP 연결 등이 전부 소켓 기반이다. 같은 머신 내부에서도 소켓을 쓸 수 있지만, 이 경우엔 파이프가 더 단순하다.

같은 서버 안에서 프로세스끼리 간단히 데이터를 흘려보내야 한다면 파이프를, 네트워크를 통해 다른 서버나 클라이언트와 통신해야 한다면 소켓을 선택한다.

profile
화이팅!

0개의 댓글