[Soc] 멀티프로세스 기반 서버 구현

2013. 7. 19. 16:49Soc

 

 

 멀티 프로세스 기반 서버구현

 

  다중 접속 서버 ; 다중 접속 서버 : 둘 이상의 클라이언트에게 동시에 서비스를 제공하는 서버

 # 다중 접속 서버 구현 방법  
  - 멀티프로세스 기반 서버    ; 다수의 프로세스를 생성하는 방식으로 서비스를 제공
  - 멀티플레싱 기반 서버       ; 입출력 대상을 묶어서 관리하는 방식으로 서비스 제공 
  - 멀티쓰레딩 기반 서버       ; 클라이언트의 수만큼 쓰레드를 생성하는 방식으로 서비스 제공

  1. 멀티 프로세스
  2. 좀비 프로세스와 소멸방법
  3. 멀티프로세스, 다중 접속 서버 구현 

멀티 프로세스 ; 메모리 공간을 차지한 상태에서 실행중인 프로그램 
# 프로세스
; 메모리 공간을 차지한 상태에서 실행중인 프로그램 

#include <unistd.h>
    pid_t fork(void);   
-> 성공 시 프로세스 ID, 실패 시 -1 반환

- fork 함수는 호출한 프로세스의 복사본을 생성, 생성 후 fork 함수 호출 이후 문장 실행( fork 함수의 반환 이후)
- 완전히 동일한 프로세스로, 메모리 영역까지 동일하게 복사하기에 이후의 프로그램 흐름은 fork 함수의 반환 값을 기준으로 나뉘도록 프로그래밍 해야 함
- 부모 프로세스 ; fork 함수의 반환 값은 자식 프로세스의 ID
  자식 프로세스 ; fork 함수의 반환 값은 0
( fork 함수의 다음 특징을 이용해서 프로그램의 흐름 구분 )

// 부모 프로세스의 pid = 자식 프로세스의 ID, 자식 프로세스의 pid = 0
// fork 함수 호출 이후에 부모 프로세스와 자식 프로세스가 서로 완전히 분리된 메모리 구조를 지님을 알 수 있음

# 좀비(Zombie) 프로세스 ; 프로세스가 생성후 할일을 다 하고 사라질 때 사라지지 않고 좀비가 되어 시스템의 중요한 리소스를 차지 하는 프로세스
( 이는 시스템에 부담을 주는 원인이 되기도 함 )

# 좀비 프로세스 생성이유
 
1. 인자를 전달하면서 exit를 호출하는 경우
 2. main 함수에서 return문을 실행하면서 값을 반환하는 경우
 - exit 함수로 전달되는 인자 값과 main 함수의 return 문에 의해 반환되는 값 모두 운영체제로 전달됨, 그리고 운영체제는 이 값이 자식 프로세스를 생성한 부모 프로세스에게 전달될 때까지 자식 프로세스를 소멸시키지 않는데, 이 상황에 놓여있는 프로세스가 좀비 프로세스 

자식 프로세스를 좀비 프로세스로 만드는 주체는 운영체제이다. 

 - 좀비 프로세스의 소멸 시기는 ? ; 해당 자식 프로세스를 생성한 부모 프로세스에게 exit 함수의 인자 값이나 return 문의 반환 값이 전달되어야 함
( 부모 프로세스가 가만히 있는데 운영체제가 알아서 값을 전달해주지는 않음, 부모 프로세스의 적극적 요청(함수 호출)등을 해야 운영체제는 값을 전달해 준다. ) 



PID가 12450인 프로세스의 상태가 좀비임을 알 수 있다.
( 30초가 지난뒤 부모 프로세스가 종료되면 PID 12448인 부모 프로세스와 자식 프로세스가 함께 소멸 되는것 확인 할 수 있음 )

# 좀비 프로세스 소멸 ; 자식 프로세스 소멸을 위해서 부모 프로세스가 자식 프로세스 전달 값 요청 ( 함수 이용 )
- wait 함수 
- waitpid 함수 
- signal 함수
- sigaction 함수

wait

 좀비 프로세스 소멸

 자식 프로세스 종료까지 블로킹 상태가 됨 

waitpid

 자식 프로세스 종료까지 블로킹 상대가 되지않고 0을 반환함 ( 주로 while문으로 자식프로세스 종료 기다림 )

 블로킹 상태가 되지는 않지만, 자식프로세스 종료까지 계속 대기 작업 반복함 

 signal

 계속 대기 작업하지 않고 자식 프로세스 종료시 운영체가 사요자가 지정해둔 함수를 호출해 작업함 

 유닉스 계열의 운영체제 별로 동작방식에 있어서 약간의 차이를 보일 수 있음 

 sigaction

 signal과 유사하지만, 훨씬 안정적으로 동작 ( 동작방식 차이를 보이지 않음 )

 


wait ; 좀비 프로세스 생성을 막음 

    #include <sys/wait.h>
    pid_t wait ( int * statloc );
    -> 성공 시 종료된 자식 프로세스의 ID, 실패 시 -1 반환

- wait 함수가 호출되었을 때, 이미 종료된 자식 프로세스가 있다면, 자식 프로세스가 종료되면서 전달한 값(return, exit)이 매개 변수로 전달된 주소의 변수에 정장됨 
- 해당 변수에 저장된 값이 프로세스 종료되면서 전달한 값 이외에도 다른 정보가 포함되어있어 분리 과정을 거쳐야함 
  WIFEXITED ; 자식 프로세스가 정상 종료한 경우 '참(true)'을 반환함
  WEXITSTATUS ; 자식 프로세스의 전달 값을 반환
- 호출된 시점에서 종료된 자식 프로세스가 없다면, 임의의 자식 프로세스가 종료될 때까지 블로킹 상태에 놓임 

// wait 함수의 인자로 변수 status의 주소값 전달되었다면, 밑과 같이 코드 작성 가능 

   if( WIFEXITED ( status ) ) // 정상 종료 했는가?
   { 
        puts("Normal termination");
        printf("Child pass num : %d", WEXITSTATUS( status )) ;   // 반환값?
   }



waitpid ; wait 함수의 블로킹이 문제일 경우, 좀비 프로세스 생성도 막고 블로킹 문제도 막을 수 있음 

    #include <sys/wait.h>
    pid_t waitpid ( pid_t pid, int * statloc, int options );
    -> 성공 시 종료된 자식 프로세스의 ID(또는 0) , 실패 시 -1 반환

- pid ; 종료 확이하고자 하는 자식 프로세스의 ID 전달, -1 전달시 wait 함수와 마찬가지로 임의의 자식 프로세스 종료 기다림
- statloc ; wait 함수의 statloc 과 동일한 의미
- options ; 헤더 파일 sys/wait.h 에 선언된 상수 WNOHANG을 인자로 전달하면, 종료된 자식 프로세스가 존재하지 않아도 블로킹 상태에 있지않고, 0을 반환하면서 함수를 빠져나옴 

signal ; 자식 프로세스의 종료시점을 알 지 못할때, waitpid 함수만 계속 호출할 수 없기에 사용됨
 - 자식 프로세스 종료의 인식 주체는 운영체제이기에 부모 프로세스에게 공지 해주는 역할을 맡길 수있음 
( 시그널 핸들링 ; 시그널은 특정상황이 발생했음을 알리기 위해 운영체제가 프로세스에게 전달하는 메시지를 의미, 핸들링은 해당 메시지에 맞는(정의된) 작업을 진행하는 것 )

1. 시그널 등록 
2. 해당 동작시 운영체제에서 특정 함수를 호출 

   #include <signal.h>
   void ( * signal ( int signo, void ( * func ) ( int ) ) ) ( int );
   -> 시그널 발생시 호출되도록 이전에 등록된 함수의 포인터 반환 

- 함수 이름 ; signal 
-  매개 변수 선언 ; int signo, void ( * func )(int)
 int signo 
    SIGALRM     alarm 함수호출을 통해서 등록된 시간이 다 된 상황
    SIGINT        CTRL + C 가 입력된 상황
    SIGCHLD    자식 프로세스가 종료된 상황
 void ( * func )( int )
    해당 함수를 작성시 반드시 매개변수형이 int, 반환형이 void 여야 함
- 반환형 ; 매개변수형이 int 이고 반환형이 void인 함수 포인터 

 signal (SIGCHLD, mychild );
 이런식으로 시그널이 등록되면, 등록된 시그널 발생시 운영체제는 해당 시그널에 등록된 함수를 호출해줌    

예)

   #include <unistd.h>
   unsigned int alarm( unsigned int seconds );
   -> 0 또는 SIGALRM 시그널이 발생하기까지 나아있는 시간을 초단위로 반환 

// 양의 정수를 인자로 전달시 전달된 수에 해당하는 시간이 지나서 SIGALRM 시그널이 발생, 0을 인자로 전달시 이전에 설정된 SIGALRM 시그널 발생의 예약이 취소됨 
// 위의 함수호출을 통해서 시그널의 발생을 예약만해놓고, 시그널 발생했을 때 호출되어야 할 함수 지정하지 않으면(signal 함수호출을 통해서) 프로세스가 그냥 종료됨 


// 5, 11 행에 시그널 발생시 호출되어지는 함수가 정의됨 => 이런 유형의 함수들 시그널 핸들러라 함 
// 시그널이 발생하면 sleep 함수의 호출로 블로킹 상태에 있던 프로세스가 깨어남 
( 함수의 호출을 유도하는 것은 운영체제이지만, 프로세스가 잠들어 있는 상태에서 함수가 호출 될 수는 없음 , 따라서 시그널이 발생하면 시그널에 해당하는 시그널 핸들러의 호출을 위해 sleep 함수의 호출로 블로킹 상태에 있던 프로세스는 깨어난. 그리고 한 번 깨어나면 다시 잠들지 않음 )

sigaction ; signal 함수는 유닉스 계열 운영체제 별로 동작방식에 있어 약간의 차이를 보일 수 있지만, sigaction 함수는 차이를 보이지 않음 

   #include <signal.h>
   int sigaction ( int signo, const struct * act, struct sigaction * oldact );
   -> 성공시 0, 실패 시 -1 반환

- signo ; sinal 함수와 마찬가지로 시그널의 정보를 인자로 전달.
- act ; 첫 번째 인자로 전달된 상수에 해당하는 시그널 발생시 호출될 함수(시그널 핸들러)의 정보 전달
     sigaction 이라는 구조체 변수를 선언 및 초기화 해야함 
     struct sigaction
     {
        void ( *sa_handler) (int) ; // 시그널 핸들러의 함수 포인터 값(주소값)을 저장
        sigset_t sa_mask;            // 모든 비트를 0으로 초기화 //시그널 관련 옵션 및 특성 
        INT sa_flags;                   // 0으로 초기화                  // 시그널 관련 옵션 및 특성
     }

- oldact ; 이전에 등록되었던 시그널 핸들러의 함수 포인터를 얻는데 사용되는 인자, 필요 없다면 0 전달 

// 17 ; sigaction 는 sa_mask 0으로 초기화 목적

시그널 핸들링을 통한 좀비 프로세스의 소멸

 

 

 

 

 

 



# 프로세스 기반의 다중 접속 서버 구현 모델 ; 동시에 두 이상의 클라이언트에게 서비스를 제공하는 형태로 에코 서버 확장

클라이언트의 서비스 요청이 있을 때마다 에코 서버는 자식 프로세스를 생서해서 서비스를 제공함
1. 에코 서버(부모 프로세스)는 accept 함수 호출을 통해서 연결 요청을 수락
2. 이때 얻게 되는 소켓의 파일 디스크립터를 자식 프로세스를 생성해서 넘겨줌
3. 자식 프로세스는 전달받은 파일 디스크립터를 바탕으로 서비스를 제공
( 자식 프로세스는 부모 프로세스가 소유하고 있는 것을 전부 복사 , 파일 디스크립터를 넘기는 과정은 별도로 거칠 필요가 없음 )

[echo_mpserv.c]


[echo_cllient] 와 함께 실행

 

 









# fork 함수 호출을 통한 파일 디스크립터 복사
- 소켓은 프로세스의  소유가 아니고, 운영체제의 소유이다. 소켓을 의미하는 파일 디스크립터만 프로세스의 소유이기에 fork 함수 호출시 자시프로세스에 복사되는 것도 프로세스 소유인 것만 복사가 됨
- "소켓이 복사되면 동일한 PORT에 할당된 소켓이 둘 이상이 됨" 따라서 소켓 복사는 이치에 맞지않음

'Soc' 카테고리의 다른 글

[soc] 소켓  (0) 2013.08.16