Download presentation
Presentation is loading. Please wait.
Published byΟὐλίξης Δημητρακόπουλος Modified 6년 전
1
인공지능실험실 석사 2학기 김승겸 codekim@ai.hannam.ac.kr
TCP/IP Socket Programming… 제 10장 멀티태스킹 기반의 서버구현 인공지능실험실 석사 2학기 김승겸
2
목차 다중 접속 서버의 구현 방법들 프로세스의 생성 프로세스 & 좀비 프로세스 시그널 핸들링 & 좀비 프로세스
Fork 함수를 이용한 다중 접속 서버의 구현 TCP 입출력 루틴 분할하기
3
다중 접속 서버의 구현 방법들 다중 접속 서버의 구현 방법 프로세스 생성을 통한 멀티태스킹 서버의 구현
select 함수에 의한 멀티플렉싱 서버의 구현 쓰레드를 기반으로 하는 멀티 쓰레팅 서버의 구현
4
프로세스의 생성 프로세스 ID 프로세스 식별자 프로세스마다 할당되는 유일한 숫자(2 ~ 32786)
프로세스 확인을 위한 ps 명령 실행
5
프로세스의 생성 fork 함수 호출을 통한 프로세스 생성 fork 함수 이용 fork를 호출한 프로세스의 복사본 생성
모든 메모리 공간을 그대로 복사 똑같은 프로그램을 완전히 독립된 프로세스가 실행 fork함수에 의한 리턴값은 다르다. 부모 프로세스 리턴값 : 자식 프로세스 ID 자식 프로세스 리턴값 : 0 #include <sys/types.h> #include <unistd.h> pid_t fork (void); 성공 시 프로세스 ID, 실패 시 -1을 리턴
6
프로세스의 생성 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; } 실행결과
7
프로세스 & 좀비 프로세스 좀비 프로세스 실행이 종료 되었지만 아직 삭제되지 않은 프로세스
프로세스가 종료하고 난 후 부모 프로세스가 종료 상태를 가져 올때가지의 프로세스 프로세스 테이블 요소를 제외하고 어떤 자원도 없는 죽은 존재이지만 계속 시스템 안에 상주한다.
8
프로세스 & 좀비 프로세스 좀비 프로세스가 생성되는 이유 자식 프로세스 부모 프로세스 커널 좀비 소멸 자식 프로세스 소멸 과정
9
프로세스 & 좀비 프로세스 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초간의 시간 동안 자식 프로세스가 좀비가 된것을 확인 할 수 있다.
10
프로세스 & 좀비 프로세스 실행결과
11
성공 시 종료된 자식 프로세스 ID, 실패 시 -1을 리턴
프로세스 & 좀비 프로세스 wait 함수의 사용 wait 함수 이용 이미 종료된 자식 프로세스가 있다면, 리턴값을 읽어들인다. 종료된 자식 프로세스가 없다면, 임의의 자식 프로세스가 종료될때까지 부모 프로세스는 블로킹 상태가 된다. wait함수의 인자인 포인터가 가리키는 변수에 여러가지 정보 저장 #include <sys/types.h> #include <sys/wait.h> pid_t wait (int * status); 성공 시 종료된 자식 프로세스 ID, 실패 시 -1을 리턴 매크로 함수 리턴 값 WIFEXITED(status) 정상 종료를 했을 경우 0을 반환한다. WEXITSTATUS(status) 종료시에 return 하거나 exit함수의 인자로 넘겨진 값을 반환한다.
12
프로세스 & 좀비 프로세스 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함수를 이용하여 좀비가 되는것을 막을 수 있다.
13
프로세스 & 좀비 프로세스 실행결과
14
성공 시 종료된 자식 프로세스 ID, 실패 시 -1을 리턴
프로세스 & 좀비 프로세스 waitpid 함수의 사용 waitpid 함수 이용 pid : 종료 확인을 원하는 자식 프로세스의 ID이다. status : 리턴된 값을 비롯해서 여러가지 정보저장 options : ‘WNOHANG’ 상수를 인자로 전달하게 되면 이미 종료된 자식프로세스가 없어도 대기 상태로 들어가지 않고 바로 리턴하게 된다. #include <sys/types.h> #include <sys/wait.h> pid_t waitpid (pid_t pid, int *status, int options); 성공 시 종료된 자식 프로세스 ID, 실패 시 -1을 리턴
15
프로세스 & 좀비 프로세스 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인자를 주어서 이미 종료된 자식이 없어도 대기 상태로 들어가지 않고 바로 리턴한다.
16
프로세스 & 좀비 프로세스 실행 결과
17
시그널 핸들링 & 좀비 프로세스 시그널 핸들링 Operatinn System signal Process 시그널 핸들링 과정
1. 특정상황발생 2. 시그널 전송 Operatinn System 3. 시그널 처리 함수 호출 signal Process 시그널 핸들링 과정 ■ 시그널 : 시스템에 특정 상황이 발생했을때 , 이를 알리기 위해 운영체제가 전달하는 메시지 ■ 시그널 핸들러 : 시그널을 처리하는 함수 모듈 ■ 시그널 핸들링 : 시그널 발생시, 해당 시그널 핸들러를 실행시키는 행위
18
시그널 핸들링 & 좀비 프로세스 시그널의 종류 시그널 발생 상황 SIGALRM
시간을 예약해 놓고 그 시간이 되었을 경우 발생한다. SIGINT 인터럽트 발생을 알린다. 여기서 인터럽트는 Ctrl+C 을 누른 경우 발생한다. SIGCHLD 자식 프로세스가 종료된 경우 발생한다.
19
리턴값은 이전 시그널 핸들러 함수의 주소값, 실패시 SIG_ERR
시그널 핸들링 & 좀비 프로세스 signal 함수를 이용한 시그널 핸들링 signal 함수 이용 시그널이 발생되었을때 시그널 핸들러 함수를 호출하여 시그널을 처리한다 int signum : 시그널 상수 func : 시그널 핸들러 함수 #include <signal.h> void (*signal(int signum, void(*func)(int)))(int); 리턴값은 이전 시그널 핸들러 함수의 주소값, 실패시 SIG_ERR
20
시그널 핸들링 & 좀비 프로세스 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);
21
SIGINT(cltr + c) 발생시 시그널 핸들러 함수에 의해 처리되는 모습
시그널 핸들링 & 좀비 프로세스 실행 결과 SIGINT(cltr + c) 발생시 시그널 핸들러 함수에 의해 처리되는 모습
22
시그널 핸들링 & 좀비 프로세스 sigaction 함수를 이용한 시그널 핸들링 sigaction 함수 이용
signum : signal 함수와 마찬가지로 가로 채고자 하는 시그널의 종류를 인자로 전달하게된다. act : 새로 등록할 시그널 핸들러 정보로 초기화된 sigaction 구조체 변수의 포인터를 인자로 전달하게 된다. oldact : 이전에 등록되었던 시그널 핸들러의 포인터를 얻고자 할때 사용되게 하는 인자이다. #include <signal.h> int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); 리턴 값은 성공시 0을, 실패 시 -1을 리턴한다.
23
시그널 핸들링 & 좀비 프로세스 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);
24
SIGINT(cltr + c) 발생시 시그널 핸들러 함수에 의해 처리되는 모습
시그널 핸들링 & 좀비 프로세스 실행 결과 SIGINT(cltr + c) 발생시 시그널 핸들러 함수에 의해 처리되는 모습
25
시그널 핸들링 & 좀비 프로세스 시그널을 통한 좀비 프로세스의 소멸(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));
26
SIGCHLD 신호를 받고 시그널 핸들러 함수에 waitpid함수를 써서 자식의 종료상태를 가져옴으로써 좀비를 제거한다.
시그널 핸들링 & 좀비 프로세스 실행 결과 SIGCHLD 신호를 받고 시그널 핸들러 함수에 waitpid함수를 써서 자식의 종료상태를 가져옴으로써 좀비를 제거한다.
27
fork함수를 이용한 다중접속 서버구현 다중 접속 서버 Echo Server Echo Client Process
1. 연결요청 Echo Server Echo Client 2. 프로세스 생성 3. 연결완료 Process Echo Client Process 다중 접속 서버 모델
28
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); }else if(pid > 0) { // 부모 프로세스 puts(“연결생성”); }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(“연결종료”); 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));
29
fork함수를 이용한 다중접속 서버구현 실행 결과 SERVER Client 1 Client 2
30
TCP 입 출력 루틴 분할하기 입 출력 분리 Echo Client Echo Server 부모 프로세스 자식 프로세스
socket READ 부모 프로세스 fork WRITE 자식 프로세스 다중 접속 서버 모델
31
TCP 입 출력 루틴 분할하기 데이터 송수신 방법의 비교 데이터 송수신 방법의 비교 Client Server Client
에코 데이터 단일 프로세스 입출력 입출력 프로세스의 분리 데이터 송수신 방법의 비교
32
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;
33
참고문헌 “UNIX Network Programming”, W.Richard Stevens
“TCP/IP 소켓 프로그래밍”, 윤성우 저
34
Q & A
Similar presentations