들어가며
프로세스 간 통신(IPC)을 공부하다 보면 가장 먼저 만나는 개념이 Pipe입니다.
그런데 Pipe는 부모-자식 프로세스처럼 혈연관계가 있는 프로세스들끼리만 통신할 수 있다는 제약이 있죠.
만약 완전히 독립적인 두 프로그램이 통신하려면 어떻게 해야 할까요?
바로 여기서 FIFO(Named Pipe)가 등장합니다.
이 글에서 FIFO의 개념부터 실제 구현까지, 직접 코드를 작성하며 겪은 문제들과 해결 과정을 공유하겠습니다.
FIFO란?
FIFO = First In First Out
FIFO는 "First In First Out"의 약자로, 먼저 들어간 데이터가 먼저 나오는 자료구조입니다.
마치 은행 대기줄처럼 선착순으로 처리되죠.
입구 → [A][B][C][D] → 출구
↑ ↓
나중에 먼저
들어감 나감
Named Pipe = FIFO
Linux에서 FIFO는 "Named Pipe"라고도 불립니다.
왜냐하면 일반 Pipe와 달리 파일시스템에 이름을 가진 파일로 존재하기 때문입니다.
$ mkfifo /tmp/myfifo
$ ls -l /tmp/myfifo
prw-rw-r-- 1 user user 0 Feb 21 10:00 /tmp/myfifo
↑
p = pipe (FIFO)
파일 타입이 `p`로 표시되는 것을 볼 수 있습니다. 이것이 바로 FIFO 파일입니다.
Pipe vs FIFO
두 개념을 비교하면 차이가 명확해집니다.
| 특징 | Pipe | FIFO (Named Pipe) |
| 이름 | 익명 (이름 없음) | 파일명으로 접근 |
| 프로세스 관계 | 부모-자식 관계 필수 | 완전히 독립적 |
| 파일시스템 | 존재하지 않음 | 파일로 존재 |
| 생성 방법 | pipe() | mkfifo() |
| 사용 예시 | ls | grep | 독립 프로그램 간 통신 |
Pipe의 제약
// Pipe는 fork() 필수
int pipefd[2];
pipe(pipefd);
fork(); // 부모-자식 관계!
if (pid == 0) {
// 자식: read
} else {
// 부모: write
}
FIFO의 자유로움
// 프로세스 A (writer.c)
int fd = open("/tmp/myfifo", O_WRONLY);
write(fd, "Hello", 5);
// 프로세스 B (reader.c) - 완전히 독립!
int fd = open("/tmp/myfifo", O_RDONLY);
read(fd, buf, 100);
실전: FIFO Chat 만들기
이론만으로는 부족하죠. 직접 간단한 채팅 프로그램을 만들어 봅시다.
1단계 : FIFO 생성
#include <sys/stat.h>
#define FIFO_PATH "/tmp/chat_fifo"
// FIFO 생성
if (mkfifo(FIFO_PATH, 0666) == -1) {
perror("mkfifo");
exit(1);
}
// 권한: 0666 = rw-rw-rw-
2단계 : Writer 프로그램
// fifo_writer.c
int main(void)
{
char message[256];
int fd;
// FIFO 열기 (쓰기 모드)
fd = open(FIFO_PATH, O_WRONLY);
while (1) {
printf("You: ");
fgets(message, sizeof(message), stdin);
if (strcmp(message, "quit\n") == 0)
break;
// 메시지 전송
write(fd, message, strlen(message) + 1);
}
close(fd);
return 0;
}
3단계 : Reader 프로그램
// fifo_reader.c
int main(void)
{
char message[256];
int fd;
// FIFO 열기 (읽기 모드)
fd = open(FIFO_PATH, O_RDONLY);
while (1) {
ssize_t n = read(fd, message, sizeof(message));
if (n == 0) { // EOF
printf("Writer disconnected\n");
break;
}
printf("Received: %s", message);
}
close(fd);
unlink(FIFO_PATH); // FIFO 삭제
return 0;
}
실행 방법
# 터미널 1
$ gcc fifo_reader.c -o reader
$ ./reader
Opening FIFO...
(Waiting for writer...)
# 터미널 2
$ gcc fifo_writer.c -o writer
$ ./writer
Connected!
You: Hello!
You: How are you?
You: quit
길이 프로토콜
실무에서는 메시지 길이를 먼저 보내는 프로토콜을 많이 사용합니다.
// Writer
uint32_t msg_len = strlen(message) + 1;
write(fd, &msg_len, sizeof(msg_len)); // 길이 먼저
write(fd, message, msg_len); // 메시지
// Reader
uint32_t msg_len;
read(fd, &msg_len, sizeof(msg_len)); // 길이 먼저
read(fd, message, msg_len); // 정확히 msg_len만큼
이렇게 하면 메시지 경계를 정확히 알 수 있어서 훨씬 안정적입니다.
FIFO의 특성
Blocking I/O
FIFO의 기본 동작은 blocking입니다.
// Writer
fd = open("/tmp/myfifo", O_WRONLY);
// ↑ Reader가 열 때까지 블로킹!
// Reader
fd = open("/tmp/myfifo", O_RDONLY);
// ↑ Writer가 열 때까지 블로킹!
동작 순서:
T0: Reader: open() 호출 → 블로킹...
T1: Writer: open() 호출
T2: 둘 다 연결됨!
T3: Reader: open() 리턴
T4: Writer: open() 리턴
EOF 처리:
Writer가 FIFO를 닫으면 Reader는 EOF를 받습니다.
// Writer
write(fd, "Last message", 13);
close(fd); // EOF 전달!
// Reader
while (1) {
n = read(fd, buf, size);
if (n == 0) { // EOF
printf("Writer disconnected\n");
break;
}
process(buf);
}
실무 활용 사례
1. 로그 수집 시스템
Application → [FIFO] → Log Collector → File/DB
여러 애플리케이션이 하나의 FIFO에 로그를 쓰면
Log Collector가 중앙에서 수집/저장
2. 프로세스 간 명령 전달
Application → [FIFO] → Log Collector → File/DB
여러 애플리케이션이 하나의 FIFO에 로그를 쓰면
Log Collector가 중앙에서 수집/저장
3. 파이프라인 구축
$ mkfifo /tmp/data_pipe
$ producer > /tmp/data_pipe &
$ consumer < /tmp/data_pipe
주의 사항
1. FIFO는 단방향
양방향 통신이 필요하면 FIFO 2개를 만들어야 합니다.
mkfifo("/tmp/fifo_to_server", 0666);
mkfifo("/tmp/fifo_to_client", 0666);
// Client
write(fd_to_server, request, len);
read(fd_from_server, response, len);
2. 정리 필수
프로그램 종료 시 FIFO 파일을 삭제해야 합니다.
close(fd);
unlink(FIFO_PATH);
3. SIGPIPE 조심
Reader가 먼저 종료하면 Writer가 SIGPIPE 시그널을 받아 종료될 수 있습니다.
// 해결책: SIGPIPE 무시
signal(SIGPIPE, SIG_IGN);
// 또는 에러 처리
if (write(fd, data, len) == -1) {
if (errno == EPIPE) {
printf("Reader closed\n");
}
}
성능 비교
FIFO vs Shared Memory 벤치마크 (1MB 데이터 전송):
FIFO: 5-10 ms
Shared Memory: 0.5-1 ms
→ FIFO는 SHM의 약 10배 느림
이유는 FIFO는 Kernel을 경유하기 때문입니다.
FIFO:
Process A → [User Space]
↓
[Kernel Space] (복사!)
↓
[User Space] → Process B
Shared Memory:
Process A ↔ [Shared Memory] ↔ Process B
(복사 없음!)
속도가 중요하다면 Shared Memory를, 간단한 통신이면 FIFO를 사용하는 게 좋습니다.
디버깅 팁
FIFO 상태 확인
# FIFO 파일 확인
$ ls -l /tmp/myfifo
prw-rw-r-- 1 user user 0 Feb 21 10:00 /tmp/myfifo
# FIFO 내용 확인 (주의: 블로킹!)
$ cat /tmp/myfifo # Reader처럼 동작
# 다른 터미널에서
$ echo "test" > /tmp/myfifo # Writer처럼 동작
strace로 시스템 콜 추적
$ strace -e trace=open,read,write ./reader
open("/tmp/chat_fifo", O_RDONLY) = 3
read(3, "Hello\0", 256) = 6
read(3, "World\0", 256) = 6
마치며
FIFO는 가장 기본적인 IPC 메커니즘 중 하나지만, 실제로 구현해 보면 생각보다 고려할 게 많습니다.
배운 점:
- Null terminator 처리의 중요성
- 버퍼 초기화 필수
- 길이 프로토콜로 견고하게
- Blocking 특성 이해
- EOF 처리
다음 공부는 Shared Memory를 공부해볼 예정입니다.
참고 자료
mkfifo(3) - Linux manual page
mkfifo(3) — Linux manual page mkfifo(3) Library Functions Manual mkfifo(3) NAME top mkfifo, mkfifoat - make a FIFO special file (a named pipe) LIBRARY top Standard C library (libc, -lc) SYNOPSIS top #include #include i
man7.org
fifo(7) - Linux manual page
fifo(7) — Linux manual page fifo(7) Miscellaneous Information Manual fifo(7) NAME top fifo - first-in first-out special file, named pipe DESCRIPTION top A FIFO special file (a named pipe) is similar to a pipe, except that it is ac
man7.org
'Dev > System Programming' 카테고리의 다른 글
| [IPC] Shared Memory - 가장 빠른 프로세스 간 통신 (0) | 2026.02.27 |
|---|
