OS

[OS] Pipe란?

kittyonthebread 2025. 2. 6. 13:12

1.  IPC (Inter Process Communication)

pipe를 알기위해서는 먼저 IPC라는 개념을 알아야한다.

IPC는 Inter Process Communication 으로 process들 간에 데이터 및 정보를 주고 받기 위한 Mechanism을 이야기한다.

Kernel에서 IPC를 위한 도구를 제공하며 System Call의 형태로 Process에게 제공된다.

 

IPC에는 크게 Shared memory와 Message Passing이라는 두 가지 모델이 있다.

 

1. Shared Memory 

  • Process의 특정 메모리 영역을 공유한다.
  • 공유한 메모리 영역에 읽기/쓰기를 통하여 통신을 수행한다.
  • 공유 메모리가 설정되면(kernel에게 할당 받음), 응용 프로그램 레벨에서 통신 기능이 제공된다(kernel 관여 X).
  • read, write 방식

2. Message Passing

  • Process간 메모리 공유 없이 동작한다.
  • kernel을 통한 메시지 통신 기능을 제공한다.
  • 구현 예시로는 pipe, message queue, socket 이 있다.
  • send, receive 방식

 

2.  Pipe

그렇다면 다시 본론으로 돌아와 pipe가 무엇인지 알아보자.

Pipe란 하나의 Process가 다른 Process로 데이터를 직접 전달하는 방법으로, pipe를 사용할 때는 기본적으로 fork() 시스템 콜이 발생한다.

데이터는 한 방향으로만 이동하며 프로세스 간 1대1 의사소통만이 가능하다.

예를 들어 Process A와 Process B가 있다고 했을 때 양방향 통신을 하기 위해서는 pipe A는 A->B, pipe B는 B->A로 데이터를 전달하는 pipe가 필요하다(Half Duplex). 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#define MAXLINE 4096

int main(void)
{
    int n, fd[2];
    pid_t pid;
    char line[MAXLINE];

    if (pipe(fd) < 0) {
        perror("pipe error");
        exit(-1);
    }

    if ( (pid = fork()) < 0) {
        perror("fork error");
        exit(-1);
    } else if (pid > 0) {
        /* parent */
        close(fd[0]);
        write(fd[1], "hello, world.\n", 14);
        waitpid(pid, NULL, 0);
    } else {
    	/* child */
        close(fd[1]);
        n = read(fd[0], line, MAXLINE);
        write(STDOUT_FILENO, line, n);
    }
    
    fflush(stdout);

    exit(0);
}
  1. fd[2]는 file descriptor를 담는 배열로 0은 read(input)용 파일 디스크립터, 1은 write(output)용 파일 디스크립터를 나타낸다.
  2. 만약 pid가 0이상이면 (== parent인 경우) input은 close 하고, "hello, world."를 output으로 보낸다.
  3. pid가 0인 child의 경우 fd[1]을 close하고(output X), 값을 읽어 n에 대입한다.
  4. write() 함수를 통해 n바이트만큼 표준 출력하면 'hello, world'가 출력된다

 

3. ls | grep .txt 

이는 ls 프로세스의 output을 grep 프로세스의 input으로 전달한 후 결과를 출력하는 명령어이다.

ls는 parent 프로세스가 되며, grep는 child 프로세스가 된다.

 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#define MAXLINE 4096

int main(void)
{
    int n, fd[2];
    pid_t pid;
    char line[MAXLINE];

    if (pipe(fd) < 0) {
        perror("pipe error");
        exit(-1);
    }

    if ( (pid = fork()) < 0) {
        perror("fork error");
        exit(-1);
    } else if (pid > 0) {
        /* parent */
        close(fd[0]);
        dup2(fd[1], STDOUT_FILENO);
        close(fd[1]);
        
        //ls 실행
        execl( "/bin/ls", "/bin/ls", NULL);
    } else {
    	/* child */
        close(fd[1]);
        dup2(fd[0], STDIN_FILENO);
        close(fd[0]);
        
	//grep 실행
        execl("/usr/bin/grep", "/usr/bin/grep", ".txt", NULL);
        
    }
    
    fflush(stdout);

    exit(0);
}

 

1) pid > 0 ( ls 실행 )

  • 가장 먼저 dup2() 함수를 실행하면 fd[1](파이프의 쓰기) 파일을 복제하여 STDOUT_FILENO 번호에 할당한다.  
  • STDOUT_FILEN에 쓰는 데이터는 파이프의 쓰기인 fd[1]로 전송이 되므로 부모 프로세스에서 실행되는 ls가 출력하는 데이터는 파이프의 쓰기 끝으로 전달된다.

 

2) pid == 0 ( grep 실행 )

  • 자식 프로세스에서는 dup2(fd[0], STDIN_FILENO)를 사용하여, 파이프의 읽기 끝을 표준 입력으로 지정한다.
  • 이 결과 파이프로 전달되는 데이터는 grep의 입력으로 들어며 해당 명령으로 처리한다.

 

3) 결과 화면 

프로그램 결과와 ls | grep .txt 결과 동일한 것을 확인할 수 있다.