프로그래밍[Univ]/기타

[시스템프로그래밍] IPC(Inter Process Communication) Part 1 Pipe & FIFO

Cloud Travel 2012. 11. 6. 22:33

* IPC(Inter Process Communication)

 - 프로세스들끼리 데이터를 주고 받는 기능을 수행

 - Pipe/Socket과 같은 것을 이용한다.


* Pipe

 - 메모리 버퍼 영역을 가르킨다.

  > 버퍼 : 각 프로세스가 통신하면서 사용하는 임시 공간

 - 각 Pipe당 읽기용 포트 한개와 쓰기용 포트 한개를 갖는다.

 - file descriptor로 접근을 하여 파일을 읽고 쓰는 듯이 사용을 한다.

 - Unix계열에서 오래전 부터 사용된 IPC형식이다.

  > Linux shell에서 "|" 기호를 사용하여 Pipe로 사용할 수 있다.

     ex) ls -al | ./a.out      : ls -al 의 결과(output)가 ./a.out의 input으로 작용한다.

 - Race Condition을 방지하기 위해서 단방향에서 쓰는 것이 일반적이다.

 - Pipe 통신을 하는 개체(Process)들은 서로 특수한 관계에 있다.(부모-자식)

  > 부모 프로세스는 자식 프로세스를 생성하기 전에 파이프를 생성하여 서로 파이프를 공유해야 한다.

 - Volatile(휘발성) : Process가 끝나면 Pipe는 자동으로 소멸하게 된다.

 - 필요한 Header file : unistd.h

 - int pipe(int fd[2])

  > 정상적으로 Pipe가 생성되면 0을 에러가 발생하면 -1을 반환한다.

  > fd[0]과 fd[1]은 각각 읽기모드와 쓰기모드의 입출구 역할을 한다.

  


 - Pipe를 생성하면 위와 같이 양방향 통신이 가능한 형태로 생성이 된다.

 - Race Condition이 우려되므로 단방향 통신형태로 바꾼다. 

  > Pipe의 용도에 따라서 Parent > Child 또는 Child > Parent 방향으로만 통신이 되게 바꾼다.

  > 양뱡향 통신을 원한다면 2개의 파이프를 이용해서 구현해야한다.

  

----------------------------------------------------------------------------------------------------------------

(예제 소스코드) - Linux Man page 


#include <sys/wait.h>

#include <assert.h>

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <string.h>


int main(int argc, char *argv[])

{

    int pfd[2];

    pid_t cpid;

    char buf;


    assert(argc == 2);


    if (pipe(pfd) == -1) { perror("pipe"); exit(EXIT_FAILURE); }


    cpid = fork();

    if (cpid == -1) { perror("fork"); exit(EXIT_FAILURE); }


    if (cpid == 0) {            /* Child reads from pipe */

        close(pfd[1]);          /* Close unused write end */


        while (read(pfd[0], &buf, 1) > 0)

            write(STDOUT_FILENO, &buf, 1);


        write(STDOUT_FILENO, "\n", 1);

        close(pfd[0]);

        _exit(EXIT_SUCCESS);


    } else {                    /* Parent writes argv[1] to pipe */

        close(pfd[0]);          /* Close unused read end */

        write(pfd[1], argv[1], strlen(argv[1]));

        close(pfd[1]);          /* Reader will see EOF */

        wait(NULL);             /* Wait for child */

        exit(EXIT_SUCCESS);

    }

}

----------------------------------------------------------------------------------------------------------------
 - FILE * popen(const char* cmdstring, const char *type);
  > cmdstring(커멘드)를 수행한 결과를 type에 따라서(읽을지 쓸지) 결과를 사용한다.
  > type : "r", "w"
  > popen한 결과로 임시 파일이 생성되는데 이는 Pipe로 생성되며, type이 "r"일 경우 결과 값이 pipe에 적히게 되고,
     type이 "w"일 경우에는 pipe에 값이 써지기를 기다린다.
  > "r"을 한 경우에는 fread(file read)함수를 이용하여 결과값을 buffer에 옮겨와 사용하며, 
     "w"인 경우에는 fwrite(file write)함수를 이용하여 특정변수 또는 buffer에 있는 값을 적어준다.

* FIFO
 - named pipe라고도 불린다.
 - pipe와 동일한 기능을 하지만, 특정 파일을 통해서 IPC를 시도한다.
  > 여러 프로세스가 접근이 가능하여 First in First out으로 service한다.
 - FIFO로 엮인 프로세스는 이제 특별한 관계일 필요가 없다.(부모-자식 프로세스가 아니여도 사용 가능)
  > 같은 System내에 있는 모든 프로세스들이 FIFO를 통해서 통신을 할 수 있다.
 - FIFO를 통해 사용되는 저장소는 file로 남아 있어서 process가 종료되도 지속적으로 유지된다.
 - 필요한 Header : sys/types.h, sys/stat.h
 - int mkfifo(const char *filename, mode_t mode);
  > FIFO를 만드는 명령어로 성공시 0을 에러시 -1을 반환한다.
  > filename : 파일 이름 // mode : 파일 생성시 사용했던 mode와 같은 값으로 rwx를 지정
 - FIFO를 생성했다면, 일반 파일을 불러와서 읽고 쓰듯이 사용하면 된다.
 - mkfifo로 생성된 file은 p 타입으로 pipe를 나타내는 파일이 된다.
 - FIFO의 주의점
  > 읽는 Process가 없는데 FIFO에 write하는 경우... : Error, SIGPIPE 신호가 생성된다.
  > PIPE_BUF(pipe buffer size)보다 써지는 data 양이 적은 경우 어떤 프로세스도 중간에 끼어들지 못한다.(atomically)
 - FIFO의 예 : Client - Server Communication
  > Client는 Server로 메세지를 보낼때 잘 알려진 FIFO(모두에게 개방되있는)를 통해서 메세지를 전달한다.
     (이 FIFO는 Client에서 Server로 향하는 단방향 Pipe이다)
  > Server는 각 클라이언트에게 특별한 FIFO를 열어서 결과를 전달해준다.
     (이 FIFO는 Server에서 Client로 향하는 단방향 Pipe이다)
  > 이러한 구조를 취하는 이유는 server로 요청되는 순서대로 일을 처리하고, 
     결과가 일을 요청한 사람에게만 전달되게 하기 위해서이다.