Recommanded Free YOUTUBE Lecture: <% selectedImage[1] %>

Contents

멀티 프로세스에 대하여

멀티 프로세스는 시스템 프로그래밍 영역으로 리눅스 시스템 프로그래밍 5장 프로세스에서 자세히 다루고 있으니 참고하기 바란다.

소켓 프로그램에서 멀티 프로세스 기술의 응용

BSD 소켓은 네트워크 통신을 위한 기술만을 제공한다. 다수의 클라이언트를 처리하기 위한 기술등은 제공하지 않는다. 소켓 프로그래밍에서 데이터 통신 이외의 기술은 독립된 기술로 존재한다. 그러므로 (다수의 클라이언트를 처리하는 등의)기능을 추가하기 위해서는 다른 기술들을 응용해야만 한다.

다수의 클라이언트를 처리하기 위한 응용 기술로서 멀티 프로세스 기술은 가장 오래된 기술 중 하나다.

이 아이디어는 소켓 서버 프로그래밍에서 "듣기 소켓"과 "연결 소켓"이 분리되어 있다는데에서 출발한다.

위 그림 처럼 듣기 소켓이 연결 소켓과 분리되어 있으므로 듣기 소켓에 의해서 새로운 클라이언트가 연결 요청을 한게 확인 되면, 새로운 (클라이언트와 데이터 통신을 하기 위한)연결 소켓을 만든다. 이제 새로운 프로세스를 만들어서 이 연결 소켓을 이용해서 클라이언트와 통신하도록 하는 것이다.

멀티 프로세스 기반 서버 프로그램 제작

우리는 셈플로 알아보는 소켓프 로그래밍 에서 단일클라이언트를 받아들이는 네트웍서버에 대해서 알아본적이 있다. 하지만 일반적인 서버는 대부분 단일 클라이언트를 받아들이기 보다는 여러개의 클라이언트를 동시에 받아들이는 경우가 대부분이다. telnet, web, ftp, smtp, nntp 등 대부부분의 서버가 한번에 여러개의 클라이언트 (이하 다중연결)를 처리한다.

이러한 다중연결을 처리하기 위해 4가지 정도의 표준적인 방법이 존재하는데, 이번장에서는 Unix 환경하에서 다중연결처리를 위해서 가장 널리 사용되는 방법인 fork(2) 에 대해서 알아보도록 하겠다.

fork는 네트워크 프로그래밍을 위한 기법이라기 보다는 일반적인 시스템플밍을 위해 사용되는 기법이고, 이를 네트웍플밍에 확대시켜서 사용한것이라고 할수 있다.

fork 는 프로세스를 복사해서 하나의 프로그램이 동시에 여러개의 업무를 처리 할수 있도록 하는 기법으로, fork 를 사용하게 되면 부모프로세스를 복사한 새로운 프로세스를 생성하게 된다. 이때 가장 먼저 실행되는 원본 프로세스를 "부모프로세스"라 하고 이 부모프로세스에게서 복사된 프로세스를 "자식프로세스" 라고 한다.

예제: fork.c
#include <unistd.h> 
#include <stdlib.h> 

int main(int argc, char **argv)
{
    int pid;    

    pid = fork();
    if (pid > 0)
    {
        printf("부모 프로세스\n");
        pause();
    }
    else if (pid == 0)
    {
        printf("자식 프로세스\n");
        pause();
    }
    else if (pid == -1)
    {
        perror("fork error : ");
        exit(0);
    }
}
프로그램을 실행하면 다음과 같은 결과가 발생할것이다.
[yundream@localhost test]# ./fork 
부모 프로세스
자식 프로세스

이제 ps 를 이용해서 프로세스 상황을 한번 알아보도록 하자.
[yundream@localhost /root]# ps -eo "%U %p %P %c"  | grep fork
yundream      1581  1483 fork
yundream      1582  1581 fork
ps 에 -eo 옵션을 사용하면 일정한 포맷에 준한 레포트출력을 가능하게 도와준다. %U 는 유저이름 %p 는 pid, %P 는 ppid, %c 는 command 를 나타낸다. 이외의 자세한 내용을 알고 싶다면 ps 의 맨페이지를 참조하기 바란다. 위의 내용을 보면 별도의 PID를 가지는 2개의 동일한 이름을 가진 프로 세스가 생겼음을 알수 있다. 그리고 1582 PID를 가지는 프로세스의 PPID를 보면 부모 프로세스의 PID가 1581 임을 알수 있고, 이를 통해서 1581 이 부모 프로세스 1582 가 자식 프로세스임을 알수 있다.

우리는 fork함수의 이러한 특성을 이용해서 다중연결을 받아들이는 서버를 제작할수 있다.

제작 할 프로그램은 에코 서버 프로그램의 멀티 프로세스 버전이다.

예제 : echo_server_mutiproc.c
#include <sys/types.h>  
#include <sys/stat.h>  
#include <sys/socket.h>  
#include <signal.h>  
#include <unistd.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  

#define MAXLINE 1024 
int main(int argc, char **argv) 
{ 
    int server_sockfd, client_sockfd; 
    int state, client_len; 
    int pid; 
 
    struct sockaddr_in clientaddr, serveraddr; 
 
    char buf[MAXLINE]; 
 
    if (argc != 2) 
    { 
        printf("Usage : %s [port]\n", argv[0]); 
	return 1;
    }  
 
    // internet 스트림 소켓을 만들도록 한다.  
    if ((server_sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) 
    { 
        perror("socket error : "); 
        exit(0); 
    }  
    bzero(&serveraddr, sizeof(serveraddr)); 
    serveraddr.sin_family = AF_INET; 
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); 
    serveraddr.sin_port = htons(atoi(argv[1])); 
 
    state = bind(server_sockfd , (struct sockaddr *)&serveraddr, sizeof(serveraddr)); 
    if (state == -1) 
    { 
        perror("bind error : "); 
        exit(0); 
    } 
     
    state = listen(server_sockfd, 5); 
    if (state == -1) 
    { 
        perror("listen error : "); 
        exit(0); 
    } 
     
    signal(SIGCHLD, SIG_IGN); 
    while(1) 
    { 
        client_sockfd = accept(server_sockfd, (struct sockaddr *)&clientaddr, 
                               &client_len); 
        if (client_sockfd == -1) 
        { 
            perror("Accept error : "); 
            exit(0); 
        } 
        printf("Accept Success!!\n");
        pid = fork(); 
        if (pid == 0)  
        { 
            while(1) 
            { 
                memset(buf, 0x00, MAXLINE); 
                if (read(client_sockfd, buf, MAXLINE-1) <= 0) 
                { 
                    close(client_sockfd); 
                    exit(0); 
                } 
                printf(" > %s", buf);
                write(client_sockfd, buf, strlen(buf)); 
            } 
        } 
        if (pid == -1) 
        { 
            perror("fork error : "); 
	    return 1;
        } 
    } 
    close(client_sockfd); 
} 

echo_server_mutiproc.c를 실행을 한 다음, 에코 클라이언트를 이용해서 테스트 하자.

fork함수는 두개 이상의 클라이언트를 제어하는 안정적인 방법을 제공해준다. 하나의 클라이언트를 처리하다가 문제가 생기더라도 해당 자식프로세스에게만 영향을 끼치므로 전체 서비스에 지장이 생길염려도 없고, 코드역시 다른 select, poll, thread 를 사용하는것에 비해서 간단하며, 흐름도 직관적이고 명확하다. 디버깅이 용이 하다라는 장점도 가지고 있다.

단점은 자식프로세스를 생성하는데, 많은 시간과 자원이 소모한다는 점이다.

만약 웹서버 처럼 연결과 종료가 빈번한 서버라면, 멀티 프로세스보다는 멀티 스레드, 입출력:::다중화(:12)등의 기술을 고려하도록 하자.