책정리/열혈 TCP,IP

11장 프로세스간 통신(Inner Process Communication)

GONII 2015. 8. 5. 16:16
  • 프로세스간 통신의 기본 개념

    프로세스간 통신의 기본 이해

    프로세스는 완전히 별개의 메모리 구조를 지닌다. 따라서 fork 함수 호출을 통해서 생성된 자식 프로세스 조차 부모 프로세스와 메모리 공간을 조금도 공유하지 않는다. 그래서 프로세스간 통신은 별도로 마련된 방법을 통해서만 이뤄질 수 있다.

    파이프(PIPE) 기반의 프로세스간 통신

    다음 그림은 프로세스간 통신의 방법으로 사용되는 파이프 기법의 구조적 모델을 보이고 있다.

    위 그림에서 보이듯이 두 프로세스간 통신을 위해서는 파이프라는 것을 생성해야 한다. 이 파이프는 프로세스에 속하는 자원이 아니다. 이는 소켓과 마찬가지로 운영체제에 속하는 자원이다. 운영체제가 마련해 주는 메모리 공간을 통해서 두 프로세스는 통신을 하게 된다.

#include <unistd.h>

int pipe(int filedes[2]);
// 성공 : 0 반환
// 실패 : -1 반환

  • filedes[0]

    파이프로부터 데이터를 수신하는데 사용되는 파일 디스크립터가 저장된다. 즉, filedes[0]은 파이프의 출구가 된다.

  • filedes[1]

    파이프로 데이터를 전송하는데 사용되는 파일 디스크립터가 저장된다. 즉, filedes[1]은 파이프의 입구가 된다.

부모 프로세스가 위의 함수를 호출하면 파이프가 생성되고, 파이프의 입구 및 출구에 해당하는 파일 디스크립터를 동시에 얻게 되는 것이다. 따라서 부모 프로세스 혼자서 파이프 안으로 데이터를 집어넣고 꺼내는 것도 가능하다. 입구 또는 출구에 해당하는 파일 디스크립터 중 하나를 자식 프로세스로 전달해야 하는데 fork 함수 호출을 통해 가능하다.

  • 예제 pipe1.c

#include <stdio.h>

#include <unistd.h>

#define BUFSIZE 30

   

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

{

int fds[2];

char str[] = "who are you?";

char buf[BUFSIZE];

pid_t pid;

   

pipe(fds);

pid = fork();

if( pid == 0 )

{

write(fds[1], str, sizeof(str));

}

else

{

read(fds[0], buf, BUFSIZE);

puts(buf);

}

   

return 0;

}

위 예제에서 보인 통신의 방법 및 경로를 그림으로 정리하면 다음과 같다. 여기서 중요한 사실은 부모, 자식 프로세스 모두 파이프의 입출력 경로에 접근이 가능하지만 자식은 입력 경로에만, 부모는 출력 경로에만 접근해서 통신을 했다는 점이다.

파이프(PIPE) 기반의 프로세스간 양방향 통신

이번에는 하나의 파이프를 통해서 두 프로세스가 양방향으로 데이터를 주고 받는 예제를 작성해보고자 한다. 이 예제에서 보이고자 하는 통신방식은 다음과 같다.

위 그림과 같이 하나의 파이프를 대상으로 양방향 통신을 하는 것도 물론 가능하다. 하지만 이러한 모델로 구현할 경우 몇 배는 더 주의해야 한다.

  • 예제 pipe2.c

#include <stdio.h>

#include <unistd.h>

#define BUFSIZE 30

   

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

{

int fds[2];

char str1[] = "who are you?";

char str2[] = "thank you for your message";

char buf[BUFSIZE];

pid_t pid;

   

pipe(fds);

pid = fork();

if( pid == 0 )

{

write(fds[1], str1, sizeof(str1));

sleep(2);

read(fds[0], buf, BUFSIZE);

printf("child proc output : %s\n", buf);

}

else

{

read(fds[0], buf, BUFSIZE);

printf("parent proc output : %s\n", buf);

write(fds[1], str2, sizeof(str2));

sleep(3);

}

return 0;

}

파이프에 데이터가 전달되면 먼저 가져가는 프로세스에게 이 데이터가 전달된다.

쉽게 말해 파이프에 데이터가 들어가면 이는 임자가 없는 데이터가 된다. 즉, read 함수 호출을 통해서 먼저 데이터를 읽어 들이는 프로세스에게 데이터가 전달된다. 그것이 비록 파이프에 데이터를 가져다 놓은 프로세스라 하더라도 말이다. 결국 부모 프로세스는 read 함수를 호출하고 나서 파이프에 데이터가 들어오기만을 기다리는 꼴이 된다. 그래서 양방향으로 데이터를 송수신하기 위해서 파이프를 두 개 생성하면 된다.

  • 예제 pipe3.c

#include <stdio.h>

#include <unistd.h>

#define BUFSIZE 30

   

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

{

int fds[2], fds2[2];

char str1[] = "who are you?";

char str2[] = "thank you your message";

char buf[BUFSIZE];

pid_t pid;

   

pipe(fds1), pipe(fds2);

pid = fork();

if( pid == 0 )

{

write(fds[1], str, sizeof(str1));

read(fds2[0], buf, BUFSIZE);

printf("child proc output : %s\n", buf);

}

else

{

read(fds1[0], buf, BUFSIZE);

printf("parent proc output: %s\n", buf);

write(fds2[1], str2, sizeof(str2));

sleep(3);

}

   

return 0;

}

  • 프로세스간 통신의 적용

    메시지를 저장하는 형태의 에코 서버

    • 예제 echo_storeserv.c

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <signal.h>

#include <sys/wait.h>

#include <arpa/inet.h>

#include <sys/socket.h>

   

#define BUFSIZE 100

void errorHandling(char* msg);

void readChildProc(int sig);

   

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

{

int servSock, clntSock;

struct sockaddr_in servAddr, clntAddr;

int fds[2];

   

pid_t pid;

struct sigaction act;

socklen_t addrSz;

int strLen, state;

char buf[BUFSIZE];

if( argc != 2 )

{

printf("usage : %s <port>\n", argv[0]);

exit(1);

}

   

act.sa_handler = readChildProc;

sigemptyset(&act.sa_mask);

act.sa_flags = 0;

state = sigaction(SIGCHLD, &act, 0);

   

servSock = socket(PF_INET, SOCK_STREAM, 0);

memset(&servAddr, 0, sizeof(servAddr));

servAddr.sin_family = AF_INET;

servAddr.sin_addr.s_addr = htonl(INADDR_ANY);

servAddr.sin_port = htons(argv[1]);

   

if( bind(servSock, (struct sockaddr*)&servAddr, sizeof(servAddr)) == -1 )

errorHandling("bind() error");

if( listen(servSock, 5) == -1 )

errorHandling("listen() error");

   

pipe(fds);

pid = fork();

if( pid == 0 )

{

FILE *fp = fpoen("echomsg.txt", "wt");

char msgbuf[BUFSIZE];

int i, len;

   

for( i = 0 ; i < 10 ; i++ )

{

len = read(fds[0], msgbuf, BUFSIZE);

fwrite((void*)msgbuf, 1, len, fp);

}

fclose(fp);

return 0;

}

   

while(1)

{

addrSz = sizeof(clntAddr);

clntSock = accept(servSock, (struct sockaddr*)&clntAddr, &addrSz);

if( clntSock == -1 )

continue;

else

puts("new client connected...");

   

pid = fork();

if( pid == 0 )

{

close(servSock);

while((strLen = read(clntSock, buf, BUFSIZE)) != 0 )

{

write(clntSock, buf, strLen);

write(fds[1], buf, strLen);

}

close(clntSock);

puts("client disconnected...");

return 0;

}

else

close(clntSock);

}

close(servSock);

return 0;

}

   

void errorHandling(char* msg)

{

}

void readChildProc(int sig)

{

}

 

반응형