Linux/Linux_programing

IPC - 03. pipe() 를 통한 프로세스 통신

sosal 2009. 12. 18. 21:23
반응형

/*
 http://sosal.tistory.com/
 * made by so_Sal
 */



각각의 프로세스는 독립적인 메모리 영역을 가지고 있다.
fork() 함수를 쓰더라도, 그 이전의 변수들이 복사되는 것일 뿐,
부모 프로세스와 자식 프로세스가 그 변수를 공유한다던가, 정보를 주고받는 행동은
방법이 없었다.

동시에 2개의 프로그램을 실행시키는데, 변수를 공유하려면 어떻게 해야할까?
공유메모리 기법을 쓰는 방법이 있고, pipe를 통해 변수를 공유한다기 보다
정보를 주고 받는 방법이 있다.

여기 포스팅되는 글은 단순히 buffer를 주고받을 수 있는 pipe에 대해서 설명하려 합니다.
부모프로세스에서 buffer 문자열을 자식프로세스에게 줄 수 있고,
자식 프로세스 역시 답장 가능입니다. ㅋ
(아참, 이 포스트를 보기 전에 fork()에 대해서 좀 아시구 오셔야 합니다.)

당장 어떻게 생긴놈인지부터 볼까요?

#include<unistd.h>
int pipe(int fd[2]);


앜.. 일단 헤더는 unistd.h군요.. 리눅스/유닉스 프로그래밍을 하는 사람이라면
빼놓을 수 없는 헤더파일입니다.
pipe 함수의 리턴값은 int. 실패했는지 확인하기 위해 쓰입니다.
그럼, 안에있는 인자는 무엇을 뜻할까요?
int형 배열 2개가 선언되어있는데, 단순 이름이 file descripter를 나타내는 fd 군요.
그렇다면 pipe함수는 파일 서술자 2개를 가지고 어떤짓을 한다는건데,
fd[0] , fd[1] 2개가 어떤식으로라도 파일 서술자가 되게 만들어
그것을 통해 정보를 주고받는 거라고 예상(?) 할 수 있습니다. ( ??.. 천천히 갑시다 ㅠㅠ)

무슨뜻인지 이해도 잘 안가는 글을 보기보다는 일단 예제를 봅시다. ㅠㅠ

<모든 예외처리는 다 생략..하였습니다. 짧고 굵은 글을 원하는 sosal입니다. ㅋㅋ>
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>

int main(void){
        char buffer[BUFSIZ]; //아까 데이터를 주고받는다고 했으니 일단 버퍼공간을 생성합니다.
        int fd[2];
        pipe(fd)==-1; //으잌ㅋ? 파이프를 생성합니다. fd[0]과 fd[1]이 일단 뭔진 모르지만 생겨났구요.

        pid_t pid;
        pid = fork(); //fork함수를 통해 정보를 주고받을 2개의 프로세스 객체를 생성합니다.

        if(pid==0){ //자식 프로세스의 경우//
                write(fd[1],"This letter is from child",BUFSIZ); 
                exit(0); //write로 fd[1]에다가 뭔가를 썼습니다. ;; 그럼 부모는?
        }
        else{ //부모 프로세스의 경우//
                read(fd[0],buffer,BUFSIZ); //부모가 fd[0]으로 읽네요? fd[0]은 아무것도 적혀있지 않는데...
                printf("Output of parent process :: %s \n\n",buffer);
                exit(0);


        }

        exit(0);
}

하아.. 결과물이 엄청 신기합니다. 자식이 fd[1]에다가 write했던 그 문자열 This letter is from child!
그대로 부모가 fd[0]으로 읽어버렸습니다. 헐..

pipe는 2개의 파일 서술자를 묶어버립니다.
예제와 결과만 보고 추측(? 능력이 된다면)할 수 있듯,
pipe를 통해 묶여진 2개의 서술자들중, 하나 (fd[1])은 write하고, 다른 하나 (fd[0])은 read 하네요.
그렇다면, 우리 암기할 시간이 왔습니다.
pipe로 연결된 2개의 배열형 파일 서술자는

1에 쓰고 0으로 읽는다.
1에 쓰고 0으로 읽는다.
1에 쓰고 0으로 읽는다.
1에 쓰고 0으로 읽는다.

이거 하나면 파이프는 끝장입니다.

위 예제에서는 자식이 fd[1]로 쓰고 부모가 fd[0]으로 읽었는데,
부모도  1에다 쓴다면 자식이 0으로 읽기가 가능하죠
그말은 즉, 자식, 부모 모두가 쓰고 읽을 수 있는
양방향 통신이라는 것입니다. !!

그럼 하나더 예제를 볼까요?
<이번 예제에서도 예외처리는 빠지되, 양방향 통신이 가능하단걸 보여주기 위한 소스입니다.>
<자식이 일단 쓰고, 부모가 읽고, 부모가 쓰고, 자식이 읽고 끝! 하는 프로그램입니다.>
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>

int main(void){
    char buffer[BUFSIZ];
    int fd[2];
    pipe(fd);

    pid_t pid;
    pid = fork();

    if(pid==0){ //자식 프로세스의 경우//
        write(fd[1],"This letter is from child",BUFSIZ);
        read(fd[0],buffer,BUFSIZ);
        printf("Output of child process :: %s \n\n",buffer);
    }
    else{ //부모 프로세스의 경우//
        read(fd[0],buffer,BUFSIZ);
        printf("Output of parent process :: %s \n\n",buffer);
        write(fd[1],"This letter is from parent.",BUFSIZ);


    }

    exit(0);
}

아니! 양방향 통신이라면서.. 이게 무슨...
child가 썼는데 무슨 child가 읽었다고 출력이 나오고
프로그램이 끝나지가 않아요! ㅠㅠ

왜그럴까요?
자식 프로세스의 소스를 봅시다.
    if(pid==0){ //자식 프로세스의 경우//
        write(fd[1],"This letter is from child",BUFSIZ);
        read(fd[0],buffer,BUFSIZ);
        printf("Output of child process :: %s \n\n",buffer);
    }

자식이 엄청 빠르게 fd[1]에 써놓고 fd[0]으로 또 읽으니
부모가 자식의 글을 읽고, 다른 글을 자식에게 전송하는 과정은... 생략되 버리는거죠
이해가 되십니까!! ㅠㅠ fd라는 파일서술자는
누가 먼저 가져가면 임자 !
이것 때문에 부모프로세스가 fd에는 어떠한 내용도 저장되어있지 않는데
read만 죽창 하다가.. 영원히 꺼지지 않는, 아들을 향한 영원한 사랑을 보내는 부모 프로세스가 되어
ctrl + c로 우리가 슬프게 꺼야만 하는 프로세스가 만들어지게 되는것입니다. (뭔소리야 ㅋㅋ)

따라서
    if(pid==0){ //자식 프로세스의 경우//
        write(fd[1],"This letter is from child",BUFSIZ);
        sleep(2);
        read(fd[0],buffer,BUFSIZ);
        printf("Output of child process :: %s \n\n",buffer);
    }

sleep(2)을 통해 쓰고, 2초동안 기다도록 만들어 준다면
자식이 부모에게 쓸 편지를 보내고 그것을 자기가 받아가는 일이 없도록 만들어주면
되는거죠
더욱 좋은 방법은
pipe로 연결되는 파일서술자를 아에 2개를 만들어버려서
부모 전용, 자식 전용을 따로 따로 코딩 해주면
더욱 안전한 프로그램이 되겠습니다.

그럼 마지막으로 fork()와 pipe() 를 이용한
간단한 프로세스 통신 예제를 보고 마무리를 지읍시다! ㅎㅎ
LINK_