Pipe란?

  • OS에서 한 프로그램 프로세스에서 다른 프로세스로 정보를 전달하는 기술이다.
  • IPC통신과 달리 한 방향으로만 통신이 가능하다.
  • 파이프 자체는 fork함수에 의해 복사되지 않는다.

pipe() in c

  • 각 프로세스에는 해당하는 메모리가 독립적으로 존재하기 때문에 메모리로 통신이 불가능하다.
  • 이것은 fork()를 이용해서 child process가 생성되더라도 데이터를 주고 받을 방법이 없다는 것이다.
  • 이 때문에 존재하는 것이 pipe() 이다. 이를 통해 서로 다른 프로세스가 데이터를 주고 받을 수 있다.

pipe(int fd[2]);

  • pipe는 크기가 2인 int형 배열을 요구한다.
  • fd[0] : 함수 호출 후 fd[0]에 입력 받을 수 있는 파일 디스크립터가 담김
  • fd[1] : 함수 호출 후 데이터를 출력할 수 있는 파일 디스크립터가 담김

어렵다.. 이럴 땐 사용법부터 보자!

사용법

쓰는 내용은 fd[1]에다 적고,

write(fd[1], “내용”, 길이);

읽을 때는 fd[0]을 통해서 읽는다.

read(fd[0], buf, 길이);

예제를 보고 가자.

main()

int pipes[2] ;

int main() {
  pid_t child_pid ;
  int exit_code ;
  
  if (pipe(pipes) != 0) {
    perror("Error") ;
    exit(1) ;
  }
  printf("%d %d\n", pipes[0], pipes[1]) ;
  
  child_pid = fork() ;
  if (child_pid == 0) {
    child_proc() ;
  }
  else {
    parent_proc() ;
  }
  wait(&exit_code) ;
  
  exit(0) ;
}
  • Line 1: pipe[2]라는 어레이를 준비한다.
  • Line 7: pipe()로 어레이에 파일 디스크립터를 넣어준다.
  • Line 11: 각각의 파일 디스크립터를 출력한다.
  • Line 13: fork함수를 통해 child_pid를 생성한다.

void parent_proc() - input을 파이프를 통해서 보내줌

void parent_proc() {
  char * buf = 0x0 ;
  ssize_t s ;
  size_t len = 0 ;
  
  close(pipes[0]) ;
  
  while ((s = getline(&buf, &len, stdin)) != -1) {
    buf[s - 1] = 0x0 ;
    
    ssize_t sent = 0 ;
    char * data = buf ;
    
    while (sent < s) {
      sent += write(pipes[1], buf + sent, s - sent) ;
    }
  
    free(buf) ;
    buf = 0x0 ;
    len = 0 ;
  }
  close(pipes[1]) ;
}
  • Line 6: 읽지 않을 것이므로 읽기용 파이프를 닫는다.
  • Line 8: getline()함수를 통해서 인풋을 읽는다.
  • Line 15: pipes[1]을 통해서 쓰기를 진행한다.
  • Line 22: 쓰기용 pipe를 닫는다.

void child_proc() - 파이프로 전달된 내용을 프린트함

void child_proc() {
  char buf[32] ;
  ssize_t s ;
  
  close(pipes[1]) ;
  
  while ((s = read(pipes[0], buf, 31)) > 0) {
    buf[s + 1] = 0x0 ;
    printf(">%s\n", buf) ;
  }
  exit(0) ;
}
  • Line 5: 쓰기용 파이프를 닫아준다.
  • Line 7: 파이프의 내용을 읽어들인다.
  • Line 11: 아예 프로세스를 종료해라.

최종 코드

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>

int pipes[2] ;

void parent_proc() {
  char * buf = 0x0 ;
  ssize_t s ;
  size_t len = 0 ;
  
  close(pipes[0]) ;
  
  while ((s = getline(&buf, &len, stdin)) != -1) {
    buf[s - 1] = 0x0 ;
    
    ssize_t sent = 0 ;
    char * data = buf ;
    
    while (sent < s) {
      sent += write(pipes[1], buf + sent, s - sent) ;
    }
  
    free(buf) ;
    buf = 0x0 ;
    len = 0 ;
  }
  close(pipes[1]) ;
}

void child_proc() {
  char buf[32] ;
  ssize_t s ;
  
  close(pipes[1]) ;
  
  while ((s = read(pipes[0], buf, 31)) > 0) {
    buf[s + 1] = 0x0 ;
    printf(">%s\n", buf) ;
  }
  exit(0) ;
}

int main() {
  pid_t child_pid ;
  int exit_code ;
  
  if (pipe(pipes) != 0) {
    perror("Error") ;
    exit(1) ;
  }
  printf("%d %d\n", pipes[0], pipes[1]) ;
  
  child_pid = fork() ;
  if (child_pid == 0) {
    child_proc() ;
  }
  else {
    parent_proc() ;
  }
  wait(&exit_code) ;
  
  exit(0) ;
}

실행 내용

jeongjinhyeog-ui-MacBook-Pro:IPC jeongjinhyeog$ gcc pipe.c -o pipe
jeongjinhyeog-ui-MacBook-Pro:IPC jeongjinhyeog$ ./pipe
3 4
Hello world!
>Hello world!

“Hello world!”는 parent가 “>Hello world!”는 child가 출력한 것이다. 데이터가 잘 전달되었다!!

느낀점

  • 왜 파이프가 필요한지는 알 수 있었다.
  • 양방향 통신이 궁금하다!