C 언어로 이름 있는 파이프(FIFO) 생성 및 활용 방법

이름 있는 파이프(FIFO)는 프로세스 간 통신(IPC)을 구현하기 위한 특별한 형태의 파일입니다. 일반적인 파일과 달리 FIFO는 데이터의 순차적 흐름을 보장하며, 주로 한 프로세스가 데이터를 쓰고 다른 프로세스가 이를 읽는 방식으로 사용됩니다. 본 기사에서는 FIFO의 개념부터 C 언어로 이를 생성하고 활용하는 방법까지 자세히 설명합니다.

이 기사를 통해 FIFO를 활용한 효율적인 프로세스 간 데이터 교환 방법을 이해할 수 있을 것입니다.

이름 있는 파이프(FIFO)란 무엇인가


이름 있는 파이프(FIFO, First In First Out)는 프로세스 간 통신(IPC)을 구현하기 위한 매커니즘 중 하나로, 두 개 이상의 프로세스가 데이터를 교환할 수 있는 특별한 파일입니다.

FIFO의 특징


FIFO는 다음과 같은 특징을 가집니다:

  • 순차적 데이터 처리: 데이터를 작성한 순서대로 읽습니다.
  • 파일 시스템 기반: FIFO는 파일 시스템에 실제 파일로 존재하며, 일반 파일과 비슷한 방식으로 읽기와 쓰기가 가능합니다.
  • 일시적 연결: 데이터를 읽고 쓰는 두 프로세스는 동시에 실행되어야 통신이 가능합니다.

FIFO와 일반 파이프의 차이

  • 일반 파이프는 부모-자식 관계의 프로세스 간에만 사용되지만, FIFO는 이름을 통해 독립적인 프로세스 간에도 사용 가능합니다.
  • FIFO는 파일 시스템에 이름을 가지므로, 해당 이름을 통해 여러 프로세스가 접근할 수 있습니다.

FIFO의 활용 사례

  • 데이터 스트리밍: 생산자-소비자 모델에서 데이터 스트리밍 처리.
  • 로그 전달: 시스템 프로세스 간에 로그 데이터를 실시간으로 전송.
  • 간단한 메시징: 프로세스 간에 간단한 메시지를 전달.

FIFO는 프로세스 간 통신에서 중요한 도구로, 효율적이고 신뢰성 있는 데이터 교환을 가능하게 합니다.

FIFO 생성 방법

C 언어에서는 이름 있는 파이프(FIFO)를 생성하기 위해 mkfifo 함수를 사용합니다. 이 함수는 파일 시스템에 FIFO 파일을 생성하며, 생성된 FIFO는 일반 파일처럼 보이지만 IPC를 위해 설계되었습니다.

mkfifo 함수


mkfifo 함수의 기본 구문은 다음과 같습니다:

#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);
  • pathname: 생성할 FIFO 파일의 경로와 이름입니다.
  • mode: 파일 권한을 나타내며, 읽기/쓰기 권한 등을 설정할 수 있습니다.
  • 반환값: 성공하면 0을 반환하며, 실패하면 -1을 반환하고 errno에 오류를 설정합니다.

mkfifo 사용 예제


다음은 mkfifo를 사용해 FIFO를 생성하는 간단한 예제입니다:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>

int main() {
    const char *fifo_path = "/tmp/my_fifo";

    // FIFO 생성
    if (mkfifo(fifo_path, 0666) == -1) {
        if (errno != EEXIST) {
            perror("mkfifo");
            return 1;
        }
    }

    printf("FIFO '%s' 생성 완료.\n", fifo_path);
    return 0;
}

코드 설명

  1. /tmp/my_fifo라는 경로에 FIFO를 생성합니다.
  2. 파일 권한은 0666(읽기 및 쓰기 권한)을 지정합니다.
  3. mkfifo 함수가 실행되고, FIFO가 이미 존재하는 경우에는 오류를 무시합니다.
  4. 생성 성공 메시지를 출력합니다.

파일 생성 결과


FIFO가 성공적으로 생성되면 지정한 경로에 FIFO 파일이 나타납니다. 예를 들어, /tmp/my_fifo는 다음과 같은 형태로 표시됩니다:

$ ls -l /tmp/my_fifo
prw-r--r-- 1 user group 0 날짜 /tmp/my_fifo

여기서 p는 FIFO 파일임을 나타냅니다.

주의사항

  • FIFO 파일은 생성 후 프로세스 간 데이터 전송을 위해 읽기 및 쓰기 작업이 필요합니다.
  • 파일 권한 설정을 통해 접근 제어를 적절히 관리해야 합니다.

FIFO를 생성한 후에는 이를 활용한 데이터 교환이 가능합니다. 다음 항목에서는 FIFO의 읽기 및 쓰기 방법에 대해 다룹니다.

FIFO 읽기와 쓰기 기본 사용법

FIFO를 생성한 후, 두 개의 프로세스가 데이터를 주고받기 위해 각각 FIFO에 데이터를 쓰고 읽을 수 있습니다. C 언어에서는 표준 파일 입출력 함수(open, read, write, close)를 사용하여 FIFO를 제어합니다.

FIFO 쓰기


FIFO에 데이터를 쓰는 기본 예제는 다음과 같습니다:

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
    const char *fifo_path = "/tmp/my_fifo";
    int fd;

    // FIFO 열기 (쓰기 모드)
    fd = open(fifo_path, O_WRONLY);
    if (fd == -1) {
        perror("open");
        return 1;
    }

    // 데이터 쓰기
    const char *message = "Hello from writer!";
    write(fd, message, sizeof(message));
    printf("FIFO에 데이터 전송: %s\n", message);

    // FIFO 닫기
    close(fd);
    return 0;
}

FIFO 읽기


FIFO에서 데이터를 읽는 예제는 다음과 같습니다:

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
    const char *fifo_path = "/tmp/my_fifo";
    int fd;

    // FIFO 열기 (읽기 모드)
    fd = open(fifo_path, O_RDONLY);
    if (fd == -1) {
        perror("open");
        return 1;
    }

    // 데이터 읽기
    char buffer[128];
    ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
    if (bytes_read > 0) {
        buffer[bytes_read] = '\0'; // Null-terminate the string
        printf("FIFO에서 수신된 데이터: %s\n", buffer);
    } else {
        perror("read");
    }

    // FIFO 닫기
    close(fd);
    return 0;
}

작동 방식

  1. 쓰기 프로세스는 FIFO를 O_WRONLY 모드로 열어 데이터를 씁니다.
  2. 읽기 프로세스는 FIFO를 O_RDONLY 모드로 열어 데이터를 읽습니다.
  3. FIFO는 블록 모드로 작동하므로, 한 프로세스가 데이터를 쓰기 전까지 다른 프로세스는 읽기를 기다립니다.

프로세스 실행 예시

  1. 터미널 1에서 읽기 프로세스를 실행:
$ ./fifo_reader
  1. 터미널 2에서 쓰기 프로세스를 실행:
$ ./fifo_writer

출력:

  • 터미널 1: FIFO에서 수신된 데이터: Hello from writer!
  • 터미널 2: FIFO에 데이터 전송: Hello from writer!

주의사항

  • 동시 실행: FIFO는 읽기와 쓰기 프로세스가 동시에 실행되어야 정상적으로 작동합니다.
  • 버퍼 관리: 읽기와 쓰기 데이터의 크기가 다를 경우, 데이터를 잘라내거나 누락되지 않도록 버퍼 크기를 적절히 설정해야 합니다.

FIFO를 이용한 기본적인 데이터 송수신을 구현하면 다양한 IPC 활용 시나리오로 확장할 수 있습니다. 다음 항목에서는 비차단 모드 설정 방법을 다룹니다.

비차단 모드에서 FIFO 활용하기

FIFO는 기본적으로 차단 모드(blocking mode)로 작동하며, 한 프로세스가 데이터를 쓰거나 읽을 때 다른 프로세스가 해당 작업을 완료할 때까지 대기합니다. 그러나 특정 상황에서는 차단을 피하고, 비차단(non-blocking) 모드로 작동하도록 설정하는 것이 유용합니다.

비차단 모드란?


비차단 모드에서는 읽기 또는 쓰기 호출이 즉시 반환되며, 데이터가 준비되지 않은 경우에도 프로세스가 멈추지 않습니다. 이를 통해 프로그램이 대기 시간 없이 다른 작업을 계속 수행할 수 있습니다.

O_NONBLOCK 플래그 사용


비차단 모드를 설정하려면 FIFO를 열 때 O_NONBLOCK 플래그를 사용합니다.

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
    const char *fifo_path = "/tmp/my_fifo";
    int fd;

    // FIFO 열기 (비차단 읽기 모드)
    fd = open(fifo_path, O_RDONLY | O_NONBLOCK);
    if (fd == -1) {
        perror("open");
        return 1;
    }

    // 비차단 읽기 시도
    char buffer[128];
    ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);

    if (bytes_read == -1) {
        perror("read"); // 데이터가 없을 경우 에러 반환
    } else if (bytes_read == 0) {
        printf("FIFO에 읽을 데이터가 없습니다.\n");
    } else {
        buffer[bytes_read] = '\0';
        printf("FIFO에서 읽은 데이터: %s\n", buffer);
    }

    // FIFO 닫기
    close(fd);
    return 0;
}

비차단 모드의 작동 방식

  1. 읽기 작업:
  • read 호출이 대기하지 않고 즉시 반환합니다.
  • FIFO에 데이터가 없는 경우, 반환값은 -1이며 errnoEAGAIN 또는 EWOULDBLOCK으로 설정됩니다.
  1. 쓰기 작업:
  • FIFO의 쓰기 버퍼가 가득 차면, 쓰기 호출이 실패하며 errnoEAGAIN으로 설정합니다.

실행 예제

  1. FIFO에 데이터가 없는 상태에서 위 코드를 실행하면 다음과 같은 결과가 출력됩니다:
$ ./fifo_reader
read: Resource temporarily unavailable
FIFO에 읽을 데이터가 없습니다.
  1. FIFO에 데이터를 쓰는 프로세스가 실행되면 데이터가 정상적으로 읽힙니다.

비차단 모드의 장점

  • 응답성 향상: 데이터를 기다리지 않고 프로그램의 다른 작업을 처리할 수 있습니다.
  • 효율적인 리소스 사용: 대기 시간을 제거해 CPU 사용률을 최적화합니다.

주의사항

  • 비차단 모드에서 데이터가 없거나 쓰기 버퍼가 가득 차면 에러가 발생하므로, 항상 반환값을 확인하고 적절히 처리해야 합니다.
  • 비차단 모드는 다른 이벤트 기반 프로그램 설계(예: select, poll, epoll)와 함께 사용할 때 더 효과적입니다.

비차단 모드는 고성능 애플리케이션과 실시간 시스템에 유용하며, 다음 항목에서는 다중 프로세스 환경에서 FIFO를 활용하는 방법을 설명합니다.

다중 프로세스 환경에서 FIFO

다중 프로세스 환경에서 FIFO는 여러 프로세스 간의 데이터 교환을 효과적으로 처리할 수 있는 강력한 도구입니다. FIFO는 단일 프로세스 쌍뿐만 아니라 다수의 프로세스 간 데이터 송수신을 지원합니다.

다중 프로세스 환경에서의 FIFO 동작

  1. 다중 쓰기: 여러 프로세스가 동일한 FIFO에 데이터를 쓸 수 있습니다. FIFO는 데이터의 순서를 보장하지 않으므로 적절한 데이터 구조나 프로토콜을 사용해야 합니다.
  2. 다중 읽기: 하나의 FIFO에서 여러 프로세스가 데이터를 읽는 경우, 데이터는 읽는 프로세스 간에 나누어집니다.

예제: 다중 프로세스 환경에서 FIFO 활용


다음은 다중 쓰기 프로세스와 단일 읽기 프로세스가 협력하는 예제입니다.

다중 쓰기 프로세스 (writer):

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {
    const char *fifo_path = "/tmp/my_fifo";
    int fd;

    // FIFO 열기
    fd = open(fifo_path, O_WRONLY);
    if (fd == -1) {
        perror("open");
        return 1;
    }

    // 데이터 쓰기
    char message[64];
    for (int i = 0; i < 5; i++) {
        snprintf(message, sizeof(message), "Writer %d: Message %d", getpid(), i);
        write(fd, message, sizeof(message));
        sleep(1);
    }

    close(fd);
    return 0;
}

단일 읽기 프로세스 (reader):

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>

int main() {
    const char *fifo_path = "/tmp/my_fifo";
    int fd;

    // FIFO 열기
    fd = open(fifo_path, O_RDONLY);
    if (fd == -1) {
        perror("open");
        return 1;
    }

    // 데이터 읽기
    char buffer[64];
    while (1) {
        ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
        if (bytes_read > 0) {
            buffer[bytes_read] = '\0';
            printf("Received: %s\n", buffer);
        } else if (bytes_read == 0) {
            break; // EOF
        } else {
            perror("read");
            break;
        }
    }

    close(fd);
    return 0;
}

실행 예시

  1. FIFO 생성 후, 다수의 쓰기 프로세스를 실행합니다.
  2. 단일 읽기 프로세스를 실행하여 데이터를 수신합니다.

터미널 1:

$ ./fifo_reader
Received: Writer 12345: Message 0
Received: Writer 67890: Message 1
...

터미널 2, 3:

$ ./fifo_writer

동기화 문제 해결


다중 프로세스 환경에서는 다음과 같은 동기화 문제가 발생할 수 있습니다:

  1. 데이터 충돌: 여러 프로세스가 동일한 FIFO에 동시에 데이터를 쓸 경우 메시지가 혼합될 수 있습니다.
  • 해결 방법: 데이터를 송신할 때 각 메시지를 고유하게 식별하거나, 고정된 크기의 메시지를 사용합니다.
  1. 읽기 분배: 데이터를 읽는 프로세스 간에 메시지가 고르게 분배되지 않을 수 있습니다.
  • 해결 방법: 특정 프로세스에 데이터를 할당하도록 설계하거나 다른 IPC 메커니즘과 병행 사용합니다.

활용 사례

  • 서버-클라이언트 모델: FIFO를 사용하여 서버가 여러 클라이언트와 데이터를 송수신합니다.
  • 로깅 시스템: 다수의 프로세스가 동일한 FIFO에 로그를 쓰고, 이를 하나의 프로세스가 모아서 처리합니다.

다중 프로세스 환경에서 FIFO를 사용하면 효율적인 데이터 송수신이 가능하며, 데이터 동기화 문제를 적절히 해결하면 안정적인 통신을 구현할 수 있습니다. 다음 항목에서는 FIFO와 파일 권한 관리를 다룹니다.

FIFO와 파일 권한 관리

FIFO는 파일 시스템에 존재하는 특수 파일이므로, 파일 권한 설정이 프로세스 간 통신의 보안과 안정성에 중요한 역할을 합니다. 파일 권한을 올바르게 설정하지 않으면 읽기 또는 쓰기가 불가능하거나 의도치 않은 접근이 발생할 수 있습니다.

파일 권한 기본 개념

  • 읽기 권한: FIFO 파일에서 데이터를 읽을 수 있습니다.
  • 쓰기 권한: FIFO 파일에 데이터를 쓸 수 있습니다.
  • 권한 부여 방법: FIFO 생성 시 mkfifo 함수의 mode 인수를 통해 설정하거나, 생성 후 chmod 명령어로 수정할 수 있습니다.

mkfifo를 사용한 권한 설정


mkfifo 함수의 두 번째 인수로 파일 권한을 설정합니다.

#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);

예제: 파일 생성 시 권한 설정

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>

int main() {
    const char *fifo_path = "/tmp/my_secure_fifo";

    // FIFO 생성 (읽기/쓰기 권한: 소유자와 그룹에만 허용)
    if (mkfifo(fifo_path, 0660) == -1) {
        if (errno != EEXIST) {
            perror("mkfifo");
            return 1;
        }
    }

    printf("FIFO '%s' 생성 완료 (권한: 0660).\n", fifo_path);
    return 0;
}

위 코드는 FIFO를 생성하면서 소유자와 그룹에 읽기/쓰기 권한(0660)을 부여합니다.

chmod를 사용한 권한 변경


FIFO 생성 후 권한을 변경할 수 있습니다.

$ chmod 0600 /tmp/my_secure_fifo
  • 0600: 소유자만 읽기/쓰기 가능.
  • 0660: 소유자와 그룹만 읽기/쓰기 가능.
  • 0666: 모든 사용자 읽기/쓰기 가능(권장하지 않음).

프로세스 간 보안 문제

  1. 권한 문제:
  • 권한이 적절히 설정되지 않으면 쓰기 또는 읽기 작업이 실패할 수 있습니다.
  • 권한이 너무 느슨하면 의도하지 않은 사용자가 FIFO에 접근할 수 있습니다.
  1. 권장 설정:
  • 비공개 통신: 0600 또는 0660 권한 사용.
  • 공개 통신: 0666 권한 사용(필요 시).

읽기와 쓰기 충돌 방지


다음 방법으로 읽기/쓰기 충돌을 방지할 수 있습니다:

  • 소유자 기반 접근: FIFO를 생성한 사용자만 데이터를 읽고 쓸 수 있도록 설정합니다.
  • 권한 그룹 관리: 관련 프로세스를 동일한 사용자 그룹에 추가하고, 그룹에 필요한 권한을 부여합니다.

권한 설정 확인


FIFO의 권한은 ls -l 명령어로 확인할 수 있습니다.

$ ls -l /tmp/my_secure_fifo
prw-rw---- 1 user group 0 날짜 /tmp/my_secure_fifo
  • p: FIFO 파일임을 나타냅니다.
  • rw-rw----: 소유자와 그룹만 읽기/쓰기 가능.

주의사항

  • 권한이 제대로 설정되지 않으면 프로세스 간 통신이 실패할 수 있습니다.
  • 필요 이상의 권한을 설정하면 보안 취약점이 생길 수 있습니다.

파일 권한 관리는 FIFO를 안정적이고 안전하게 사용하는 데 필수적인 부분입니다. 다음 항목에서는 FIFO 사용 시 발생할 수 있는 주요 에러와 해결 방법을 다룹니다.

에러 처리 및 디버깅

FIFO를 사용하는 동안 다양한 상황에서 에러가 발생할 수 있습니다. 이러한 문제를 적절히 처리하고 디버깅하는 것은 안정적인 프로그램 개발에 필수적입니다.

주요 에러 상황

  1. FIFO 파일 생성 실패:
  • 원인: 파일 시스템에 쓰기 권한이 없거나 FIFO가 이미 존재.
  • 해결 방법:
    • 파일 시스템 경로와 권한 확인.
    • errno 값을 확인하여 구체적인 문제를 진단.
    if (mkfifo("/tmp/my_fifo", 0666) == -1) { if (errno == EEXIST) { printf("FIFO가 이미 존재합니다.\n"); } else { perror("mkfifo"); return 1; } }
  1. FIFO 열기 실패:
  • 원인: FIFO 파일이 존재하지 않거나, 권한 부족.
  • 해결 방법:
    • 파일 경로와 접근 권한을 확인.
    • 파일 시스템에 파일이 존재하는지 확인.
    int fd = open("/tmp/my_fifo", O_RDONLY); if (fd == -1) { perror("open"); return 1; }
  1. 읽기 및 쓰기 실패:
  • 원인:
    • 쓰기 프로세스 없이 읽기만 시도(블록 모드).
    • FIFO 버퍼가 가득 참(비차단 모드).
  • 해결 방법:
    • 비차단 모드를 설정하여 블록 방지.
    • errno 값을 확인하고 적절한 에러 처리 수행.
    ssize_t bytes_read = read(fd, buffer, sizeof(buffer)); if (bytes_read == -1) { if (errno == EAGAIN) { printf("데이터가 준비되지 않았습니다.\n"); } else { perror("read"); } }

일반적인 디버깅 전략

  1. 파일 존재 여부 확인:
  • FIFO 파일이 올바른 위치에 생성되었는지 확인합니다. $ ls -l /tmp/my_fifo
  1. 권한 문제 해결:
  • FIFO 파일의 읽기/쓰기 권한을 확인하고, 필요 시 chmod로 수정합니다. $ chmod 0666 /tmp/my_fifo
  1. 프로세스 상태 점검:
  • 읽기와 쓰기 프로세스가 올바르게 실행되고 있는지 확인합니다.
  • 두 프로세스가 동시에 실행되지 않으면 FIFO 통신이 실패할 수 있습니다.
  1. 데이터 로그 출력:
  • FIFO에서 읽고 쓰는 데이터를 로그에 기록하여 예상한 대로 작동하는지 확인합니다. printf("FIFO에 쓰는 데이터: %s\n", message); printf("FIFO에서 읽은 데이터: %s\n", buffer);

주요 에러와 대응

에러 코드의미해결 방법
EACCES파일 접근 권한 부족파일 권한 확인 및 수정
EEXISTFIFO 파일이 이미 존재기존 파일 삭제 또는 기존 FIFO 사용
EAGAIN데이터 준비되지 않음 (비차단)데이터가 준비될 때까지 재시도
ENOENT파일 또는 경로 없음파일 경로를 확인하거나 새로 생성

실제 문제 시나리오


문제: FIFO에서 데이터를 읽으려고 했으나 읽기 호출이 멈춤.
원인: 쓰기 프로세스가 없어서 읽기 호출이 차단 상태.
해결 방법:

  • 쓰기 프로세스를 먼저 실행하거나, 비차단 모드로 설정.

문제: FIFO에 데이터를 썼으나, 일부 데이터가 손실됨.
원인: FIFO 버퍼 크기를 초과했거나, 데이터가 병합됨.
해결 방법:

  • 고정된 크기의 메시지 구조를 사용하여 데이터를 정렬.

디버깅 도구

  1. strace: 시스템 호출을 추적하여 FIFO 관련 호출 흐름 분석.
   $ strace ./fifo_reader
  1. lsof: FIFO 파일을 사용하는 프로세스를 확인.
   $ lsof /tmp/my_fifo

정확한 에러 처리와 디버깅은 FIFO 기반 IPC의 안정성과 신뢰성을 보장하는 데 중요합니다. 다음 항목에서는 FIFO 활용 응용 예제를 소개합니다.

FIFO 활용 응용 예시

FIFO는 다양한 프로세스 간 통신(IPC) 시나리오에서 활용될 수 있습니다. 여기서는 FIFO를 이용한 간단한 채팅 프로그램 예제를 통해 실제 활용 방법을 소개합니다.

응용 예제: 간단한 채팅 프로그램


이 예제에서는 두 프로세스가 하나의 FIFO를 사용하여 메시지를 주고받는 채팅 프로그램을 구현합니다.

Writer 프로세스 (메시지 송신자):

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main() {
    const char *fifo_path = "/tmp/chat_fifo";
    char message[128];
    int fd;

    // FIFO 열기 (쓰기 모드)
    fd = open(fifo_path, O_WRONLY);
    if (fd == -1) {
        perror("open");
        return 1;
    }

    printf("채팅 프로그램 - 송신자\n");
    printf("메시지를 입력하세요 (종료하려면 'exit' 입력):\n");

    while (1) {
        printf(">> ");
        fgets(message, sizeof(message), stdin);
        message[strcspn(message, "\n")] = '\0'; // 개행 문자 제거

        // "exit" 입력 시 종료
        if (strcmp(message, "exit") == 0) {
            break;
        }

        // 메시지 쓰기
        write(fd, message, strlen(message) + 1);
    }

    close(fd);
    return 0;
}

Reader 프로세스 (메시지 수신자):

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main() {
    const char *fifo_path = "/tmp/chat_fifo";
    char buffer[128];
    int fd;

    // FIFO 열기 (읽기 모드)
    fd = open(fifo_path, O_RDONLY);
    if (fd == -1) {
        perror("open");
        return 1;
    }

    printf("채팅 프로그램 - 수신자\n");
    printf("메시지를 기다리는 중...\n");

    while (1) {
        // 메시지 읽기
        ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
        if (bytes_read > 0) {
            buffer[bytes_read] = '\0'; // Null-terminate the string
            printf("송신자: %s\n", buffer);

            // "exit" 메시지 수신 시 종료
            if (strcmp(buffer, "exit") == 0) {
                break;
            }
        }
    }

    close(fd);
    return 0;
}

프로그램 실행 순서

  1. FIFO 생성:
   $ mkfifo /tmp/chat_fifo
  1. 터미널 1에서 수신자(reader) 실행:
   $ ./fifo_reader
   채팅 프로그램 - 수신자
   메시지를 기다리는 중...
  1. 터미널 2에서 송신자(writer) 실행:
   $ ./fifo_writer
   채팅 프로그램 - 송신자
   메시지를 입력하세요 (종료하려면 'exit' 입력):
   >> Hello!
   >> How are you?
   >> exit

출력 예시:

  • 송신자 터미널:
  >> Hello!
  >> How are you?
  >> exit
  • 수신자 터미널:
  송신자: Hello!
  송신자: How are you?
  송신자: exit

응용 시나리오

  1. 실시간 메시징 시스템: FIFO를 이용한 간단한 채팅 애플리케이션.
  2. 로그 전송: 여러 프로세스의 로그 데이터를 중앙에서 수집.
  3. 생산자-소비자 모델: 데이터를 생산하는 프로세스와 이를 소비하는 프로세스 간 통신.

한계와 개선 방안

  • 단일 방향 통신: FIFO는 기본적으로 한 방향 데이터 흐름만 지원하므로 양방향 통신을 위해 두 개의 FIFO를 사용해야 합니다.
  • 동기화 문제: 다중 쓰기/읽기 프로세스에서 데이터 손실을 방지하기 위해 고유 메시지 식별자가 필요합니다.
  • 대규모 시스템: 대규모 환경에서는 FIFO 대신 메시지 큐 또는 소켓 같은 다른 IPC 메커니즘이 더 적합할 수 있습니다.

이 예제는 FIFO의 실용적인 활용 방법을 보여주며, 간단한 데이터 통신 프로그램을 구현하는 데 유용합니다. 다음 항목에서는 전체 기사를 요약합니다.

요약

본 기사에서는 C 언어에서 이름 있는 파이프(FIFO)를 생성하고 활용하는 방법에 대해 다뤘습니다. FIFO의 개념과 생성 방법, 데이터 읽기와 쓰기 방식, 비차단 모드 설정, 다중 프로세스 환경에서의 사용, 파일 권한 관리, 에러 처리 및 디버깅, 그리고 실용적인 응용 예제를 자세히 설명했습니다.

FIFO는 프로세스 간 효율적이고 신뢰성 있는 통신을 제공하며, 적절한 설정과 관리로 다양한 시스템에서 활용할 수 있습니다. FIFO를 활용하면 IPC를 구현하는 기본적인 기술을 익히고, 더 복잡한 통신 구조로 확장할 수 있는 기반을 마련할 수 있습니다.