Contents

조회 수 12917 댓글 0
Atachment
첨부 '1'
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
일반적인 서버는 대부분 단일 클라이언트를 받아들이기 보다는 여러개의 클라이언트를 동시에 받아들이는 경우가 대부분이다.
telnet, web, ftp, smtp, nntp 등 대부부분의 서버가 한번에 여러개의 클라이언트 (이하 다중연결)를 처리한다.
이러한 다중연결을 처리하기 위해 4가지 정도의 표준적인 방법이 존재하는데, 이번장에서는 Unix 환경하에서 다중연결처리를 위해서 가장 널리 사용되는 방법인 fork(2) 에 대해서 알아보도록 하겠다.

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

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 의 이러한 특성을 이용해서 다중연결을 받아들이는 서버를 제작할수 있다. 다음은 셈플로 알아보는 소켓프로그래밍(1) 에 있는 주소록 프로그램을 다중연결 가능하도록 수정한 코드이다. 클라이언트는 위의 서버를 위해 작성한 클라이언트를 그대로 사용하도록 한다.

예제 : zipcode_multi.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> 

int main(int argc, char **argv)
{
    int server_sockfd, client_sockfd;
    int state, client_len;
    int pid;

    FILE *fp;
    struct sockaddr_in clientaddr, serveraddr;

    char buf[255];
    char line[255];

    if (argc != 2)
    {
        printf("Usage : ./zipcode [port]\n");
        printf("예    : ./zipcode 4444\n");
        exit(0);  
    } 

    memset(line, '\0', 255);
    state = 0;

    // 주소 파일을 읽어들인다.  
    client_len = sizeof(clientaddr);
    if((fp = fopen("zipcode.txt", "r")) == NULL)
    {  
        perror("file open error : ");
        exit(0); 
    } 

    // 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);
        pid = fork();
        if (pid == 0) 
        {
            if (client_sockfd == -1)
               {
                perror("Accept error : ");
                exit(0);
            }
            while(1)
            {
                memset(buf, '\0', 255);
                if (read(client_sockfd, buf, 255) <= 0)
                {
                    close(client_sockfd);
                    exit(0);
                }

                if (strncmp(buf, "quit",4) == 0)
                {
                    write(client_sockfd, "bye bye", 8);
                    close(client_sockfd);
                    exit(0);
                    break;
                }

                while(fgets(line,255,fp) != NULL)
                {
                    if (strstr(line, buf) != NULL)
                    {
                        write(client_sockfd, line, 255);
                    }
                    memset(line, '\0', 255);
                }
                write(client_sockfd, "end", 255);
                printf("send end\n");
                rewind(fp);
            }
        }
        if (pid == -1)
        {
            perror("fork error : ");
        }
    }
    close(client_sockfd);
}
zipcode_multi.c 를 컴파일한다음, port 번호를 주고 실행을 하고, 여러개의 zipcolde_cl 을 이용해서 접근을 해보자.
동시에 다수의 클라이언트를 처리한다는것을 알수 있을것이다.

fork 는 다중의 클라이언트를 제어하는 매우 확실한 방법을 제공해준다. 
하나의 클라이언트를 처리하다가 문제가 생기더라도 해당 자식프로세스에게만 영향을 끼치므로 전체 서비스에 지장이 생길염려도 없고, 코드역시 다른 select, poll, thread 를 사용하는것에 비해서 간단하며, 디버깅이 용이 하다라는 장점도 가지고 있다.
거의 유일하다고 생각되는 단점은 자식프로세스를 생성하는데, 많은 시간과 자원이 소모된다는점과, 어느정도 이상의 자식프로세스를 생성시켰을때 Unix 버젼에 따라서 생성프로세스의 수에 제한이 생긴다는 점이다.
물론 다른 방법들도(select, poll..) 이러한 제한이 있긴하지만, fork 의 경우 좀더 이러한 제한에 신경을 써주어야 한다.

?

List of Articles
번호 분류 제목 글쓴이 날짜 조회 수
205 Develop [ios] UUID 생성 + Key Chain 연동 file hooni 2016.05.13 4657
204 Develop [ios] 앱딜리게이트 라이프사이클 (AppDelegate Lifecycle) file hooni 2014.05.09 4447
203 Develop [ios] URL Scheme 이용하여 앱 설치 확인 hooni 2014.03.10 4364
202 Develop [c#]뉴 툴바 개인적으로 만든거.. (old) secret hooni 2013.04.23 4272
201 Develop [ios] 애플 앱스토어 IDFA 리뷰 정책 변경 안내 file hooni 2014.05.07 4175
200 Develop [ios] AES256 알고리즘을 이용해 데이터 암호화/복호화 방법 file hooni 2015.07.21 4097
199 Develop [ios] DatePicker iOS 6.x 이하 디자인. file hooni 2014.04.10 4052
198 Develop [ios] iOS 7 이상 UIBarButtonItem 여백 file hooni 2014.03.27 4047
197 Develop [ios] UDID 사용 제한에 따른 대안들 hooni 2014.03.13 3964
196 Develop [ios] 아이폰 GPS 사용하기 hooni 2014.05.24 3941
195 Develop [android] 코드에서 문자열로 Resource 가져오기 hooni 2015.07.09 3936
194 Develop [ios] iOS 의 인앱구매 소개 file hooni 2014.04.29 3821
Board Pagination Prev 1 ... 49 50 51 52 53 54 55 56 57 58 ... 71 Next
/ 71