340 likes | 737 Vues
TCP/IP Socket Programming…. 제 10 장 멀티태스킹 기반의 서버구현. 인공지능실험실 석사 2 학기 김승겸 codekim@ai.hannam.ac.kr. 목차. 다중 접속 서버의 구현 방법들 프로세스의 생성 프로세스 & 좀비 프로세스 시그널 핸들링 & 좀비 프로세스 Fork 함수를 이용한 다중 접속 서버의 구현 TCP 입출력 루틴 분할하기. 다중 접속 서버의 구현 방법들. 다중 접속 서버의 구현 방법 프로세스 생성을 통한 멀티태스킹 서버의 구현
E N D
TCP/IP Socket Programming… 제 10장 멀티태스킹 기반의 서버구현 인공지능실험실 석사 2학기 김승겸 codekim@ai.hannam.ac.kr
목차 • 다중 접속 서버의 구현 방법들 • 프로세스의 생성 • 프로세스 & 좀비 프로세스 • 시그널 핸들링 & 좀비 프로세스 • Fork 함수를 이용한 다중 접속 서버의 구현 • TCP 입출력 루틴 분할하기
다중 접속 서버의 구현 방법들 • 다중 접속 서버의 구현 방법 • 프로세스 생성을 통한 멀티태스킹 서버의 구현 • select 함수에 의한 멀티플렉싱 서버의 구현 • 쓰레드를 기반으로 하는 멀티 쓰레팅 서버의 구현
프로세스의 생성 • 프로세스 ID • 프로세스 식별자 • 프로세스마다 할당되는 유일한 숫자(2 ~ 32786) 프로세스 확인을 위한 ps 명령 실행
#include <sys/types.h> #include <unistd.h> pid_t fork (void); 성공 시 프로세스 ID, 실패 시 -1을 리턴 프로세스의 생성 • fork 함수 호출을 통한 프로세스 생성 • fork 함수 이용 • fork를 호출한 프로세스의 복사본 생성 • 모든 메모리 공간을 그대로 복사 • 똑같은 프로그램을 완전히 독립된 프로세스가 실행 • fork함수에 의한 리턴값은 다르다. • 부모 프로세스 리턴값 : 자식 프로세스 ID • 자식 프로세스 리턴값 : 0
프로세스의 생성 • fork.c #include<stdio.h> #include<unitd.h> #include<sys/types.h> int main( ) { pid_t pid; int data = 10; pid = fork(); if(pid == -1) printf(“fork실패, 프로세스 id : %d\n”, pid); printf(“fork성공, 프로세스 id : %d\n”, pid); if(pid == 0) // 자식 프로세스 data += 10; else // 부모 프로세스 data -= 10; printf(“data : %d\n”, data); return 0; } 실행결과
프로세스 & 좀비 프로세스 • 좀비 프로세스 • 실행이 종료 되었지만 아직 삭제되지 않은 프로세스 • 프로세스가 종료하고 난 후 부모 프로세스가 종료 상태를 가져 올때가지의 프로세스 • 프로세스 테이블 요소를 제외하고 어떤 자원도 없는 죽은 존재이지만 계속 시스템 안에 상주한다.
0 0 자식 프로세스 커널 부모 프로세스 좀비 0 0 자식 프로세스 커널 부모 프로세스 소멸 프로세스 & 좀비 프로세스 • 좀비 프로세스가 생성되는 이유 자식 프로세스 소멸 과정
프로세스 & 좀비 프로세스 • zombie.c #include<stdio.h> #include<unitd.h> #include<sys/types.h> int main( ) { pid_t pid; int data = 10; pid = fork(); if(pid == -1) printf(“fork실패, 프로세스 id : %d\n”, pid); printf(“fork성공, 프로세스 id : %d\n”, pid); if(pid == 0) // 자식 프로세스 data += 10; else { // 부모 프로세스 data -= 10; sleep(20); // 20초동안 정지 상태 printf(“data : %d\n”, data); return 0; } ■ 부모 프로세스 루틴 부분에 sleep(20)함수를 주어서 20초동안 정지상태로 만든다. ■ 자식 프로세스가 먼저 종료한 후에 부모 프로세스는 20초 후에 종료한다. ■ 20초간의 시간 동안 자식 프로세스가 좀비가 된것을 확인 할 수 있다.
프로세스 & 좀비 프로세스 • 실행결과
#include <sys/types.h> #include <sys/wait.h> pid_t wait (int * status); 성공 시 종료된 자식 프로세스 ID, 실패 시 -1을 리턴 프로세스 & 좀비 프로세스 • wait 함수의 사용 • wait 함수 이용 • 이미 종료된 자식 프로세스가 있다면, 리턴값을 읽어들인다. • 종료된 자식 프로세스가 없다면, 임의의 자식 프로세스가 종료될때까지 부모 프로세스는 블로킹 상태가 된다. • wait함수의 인자인 포인터가 가리키는 변수에 여러가지 정보 저장
프로세스 & 좀비 프로세스 • wait.c #include<stdio.h> #include<unitd.h> #include<sys/types.h> int main( ) { pid_t pid, child; int data = 10; int state; pid = fork(); if(pid == -1) printf(“fork실패, 프로세스 id : %d\n”, pid); printf(“fork성공, 프로세스 id : %d\n”, pid); if(pid == 0) // 자식 프로세스 data += 10; else { // 부모 프로세스 data -= 10; child = wait(&state); // 자식 프로세스 종료대기 printf(“자식프로세스 ID=%d\n”, child); printf(“리턴값=%d\n”, WEXITSTATUS(state)); sleep(20); // 20초동안 정지 상태 printf(“data : %d\n”, data); return 0; } ■ wait함수를 이용하여 종료된 자식프로세스의 종료상태를 읽어온다. ■ WEXITSTATUS 매크로 함수를 이용하여 자식프로세스가 종료시 리턴값을 얻는다. ■ wait함수를 이용하여 좀비가 되는것을 막을 수 있다.
프로세스 & 좀비 프로세스 • 실행결과
#include <sys/types.h> #include <sys/wait.h> pid_t waitpid (pid_t pid, int *status, int options); 성공 시 종료된 자식 프로세스 ID, 실패 시 -1을 리턴 프로세스 & 좀비 프로세스 • waitpid 함수의 사용 • waitpid 함수 이용 • pid : 종료 확인을 원하는 자식 프로세스의 ID이다. • status : 리턴된 값을 비롯해서 여러가지 정보저장 • options : ‘WNOHANG’상수를 인자로 전달하게 되면 이미 종료된 자식프로세스가 없어도 대기 상태로 들어가지 않고 바로 리턴하게 된다.
프로세스 & 좀비 프로세스 • waitpid.c #include<stdio.h> #include<unitd.h> #include<sys/types.h> int main( ) { pid_t pid, child; int data = 10; int state; pid = fork(); if(pid == -1) printf(“fork실패, 프로세스 id : %d\n”, pid); printf(“fork성공, 프로세스 id : %d\n”, pid); if(pid == 0) // 자식 프로세스 data += 10; sleep(10) // 종료를 10초 지연 else { // 부모 프로세스 data -= 10; do{ sleep(3); puts(“3초 대기”); child = waitpid(-1, &state, WNOHANG); }while(child == 0); printf(“Chid process id=%d, return value = %d \n”,child,WEXITSTATUS(state)); printf(“data : %d\n”, data); return 0; } ■ waitpid함수를 이용하여 종료된 자식프로세스의 종료상태를 읽어온다. ■ WNOHANG인자를 주어서 이미 종료된 자식이 없어도 대기 상태로 들어가지 않고 바로 리턴한다.
프로세스 & 좀비 프로세스 • 실행 결과
시그널 핸들링 & 좀비 프로세스 • 시그널 핸들링 1. 특정상황발생 2. 시그널 전송 Operatinn System 3. 시그널 처리 함수 호출 signal Process 시그널 핸들링 과정 ■ 시그널 : 시스템에 특정 상황이 발생했을때 , 이를 알리기 위해 운영체제가 전달하는 메시지 ■ 시그널 핸들러 : 시그널을 처리하는 함수 모듈 ■ 시그널 핸들링 : 시그널 발생시, 해당 시그널 핸들러를 실행시키는 행위
시그널 핸들링 & 좀비 프로세스 • 시그널의 종류
#include <signal.h> void (*signal(int signum, void(*func)(int)))(int); 리턴값은 이전 시그널 핸들러 함수의 주소값, 실패시 SIG_ERR 시그널 핸들링 & 좀비 프로세스 • signal 함수를 이용한 시그널 핸들링 • signal 함수 이용 • 시그널이 발생되었을때 시그널 핸들러 함수를 호출하여 시그널을 처리한다 • int signum : 시그널 상수 • func : 시그널 핸들러 함수
시그널 핸들링 & 좀비 프로세스 • sigint.c #include<stdio.h> #include<unitd.h> #include<signal.h> void handler(int sig); int main( ) { int state; int num = 0; signal(SIGINT, handler); // 시그널 함수 while(1) { printf(“%d : 대기중\n”, num++); sleep(2); if(num>5) break; } return 0; } void handler(int sig) { // 시그널 핸들러 함수 signal(SIGINT, handler); printf(“전달된 시그널은 %d\n”, sig); }
시그널 핸들링 & 좀비 프로세스 • 실행 결과 SIGINT(cltr + c) 발생시 시그널 핸들러 함수에 의해 처리되는 모습
#include <signal.h> int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); 리턴 값은 성공시 0을, 실패 시 -1을 리턴한다. 시그널 핸들링 & 좀비 프로세스 • sigaction 함수를 이용한 시그널 핸들링 • sigaction 함수 이용 • signum : signal 함수와 마찬가지로 가로 채고자 하는 시그널의 종류를 인자로 전달하게된다. • act : 새로 등록할 시그널 핸들러 정보로 초기화된 sigaction 구조체 변수의 포인터를 인자로 전달하게 된다. • oldact : 이전에 등록되었던 시그널 핸들러의 포인터를 얻고자 할때 사용되게 하는 인자이다.
시그널 핸들링 & 좀비 프로세스 • sigint2.c #include<stdio.h> #include<unitd.h> #include<signal.h> void handler(int sig); int main( ) { int state; int num = 0; struct sigaction act; act.sa_handler = handler; sigemptyset(&act.sa_mask); act.sa_flags = 0; state = sigaction(SIGINT, &act, 0); if(state != 0) { puts(“sigaction() error”); exit(1); } while(1) { printf(“%d : 대기중\n”, num++); sleep(2); if(num>5) break; } return 0; } void handler(int sig) { // 시그널 핸들러 함수 printf(“전달된 시그널은 %d\n”, sig); }
시그널 핸들링 & 좀비 프로세스 • 실행 결과 SIGINT(cltr + c) 발생시 시그널 핸들러 함수에 의해 처리되는 모습
시그널 핸들링 & 좀비 프로세스 • 시그널을 통한 좀비 프로세스의 소멸(zombie_handler.c) #include<stdio.h> #include<unitd.h> #include<signal.h> #include<sys/types.h> #include<sys/wait.h> void z_handler(int sig); int main( ) { pid_t pid; int state, i; struct sigaction act; act.sa_handler = z_handler; sigemptyset(&act.sa_mask); act.sa_flags = 0; state = sigaction(SIGCHLD, &act, 0); if(state != 0) { puts(“sigaction() error”); exit(1); } pid = fork(); if(pid == 0) { printf(“자식 프로세스 생성 : %d\n”, getpid()); exit(3); } else { sleep(3); } return 0; } void z_handler(int sig) { // 시그널 핸들러 함수 pid_t pid; int rtn; while((pid=waitpid(-1, &rtn, WNOHANG)) > 0) { printf(“소멸된좀비프로세스ID : %d\n”, pid); printf(“리턴된데이터 : %d=n”,WEXITSTATUS(rtn)); } }
시그널 핸들링 & 좀비 프로세스 • 실행 결과 SIGCHLD 신호를 받고 시그널 핸들러 함수에 waitpid함수를 써서 자식의 종료상태를 가져옴으로써 좀비를 제거한다.
fork함수를 이용한 다중접속 서버구현 • 다중 접속 서버 1. 연결요청 Echo Server Echo Client 2. 프로세스 생성 3. 연결완료 Process Echo Client Process 다중 접속 서버 모델
fork함수를 이용한 다중접속 서버구현 • echo_multiserv.c while(1) { addr_size = sizeof(clnt_addr); clnt_sock = accept(serv_sock, (struct sockaddr*) , &addr_size); if(clnt_sock == -1) continue; if((pid=fork()) == -1) { close(clnt_sock); continue; }else if(pid > 0) { // 부모 프로세스 puts(“연결생성”); close(clnt_sock); }else { // 자식 프로세스 close(serv_sock); while((str_len=read(clnt_sock, message, BUFSIZE)) != 0) { write(clnt_sock, message, str_len); write(1, message, str_len); } puts(“연결종료”); close(clnt_sock); exit(0); } } return 0; } void z_handler(int sig) { pid_t pid; int rtn; pid=waitpid(-1, &rtn, WNOHANG); printf(“소멸된좀비프로세스ID : %d\n”, pid); printf(“리턴된데이터 : %d=n”,WEXITSTATUS(rtn)); }
fork함수를 이용한 다중접속 서버구현 • 실행 결과 SERVER Client 1 Client 2
TCP 입 출력 루틴 분할하기 • 입 출력 분리 Echo Client Echo Server 부모 프로세스 READ socket fork WRITE 자식 프로세스 다중 접속 서버 모델
TCP 입 출력 루틴 분할하기 • 데이터 송수신 방법의 비교 Client Server Client Server 데이터 에코 데이터 단일 프로세스 입출력 입출력 프로세스의 분리 데이터 송수신 방법의 비교
fork함수를 이용한 다중접속 서버구현 • echo_multiclnt.c pid = fork(); If(pid == 0) { // 자식프로세스 (메시지 전송) while(1) { fputs(“전송할 메시지를 입력하세요: “, stdout); fgets(“message, BUFSIZE, stdin); if(!strcmp(message, “q\n”)) { shutdown(sock, SHUT_WR); close(sock); exit(0); } write(sock, message, strlne(message)); } } else { // 부모 프로세스 메시지 수신 while(1) { str_len = read(sock, message, BUFSIZE); if(str_len == 0) { exit(0); } message[str_len] = 0; printf(“서버로부터 전송된 메시지:%s\n”, message); } } close(sock); return 0; }
참고문헌 • “UNIX Network Programming”, W.Richard Stevens • “TCP/IP 소켓 프로그래밍”, 윤성우 저