1 / 26

제 17 장 멀티쓰레드 기반의 서버구현

TCP/IP Socket Programming…. 제 17 장 멀티쓰레드 기반의 서버구현. 데이터베이스 실험실 석사 2 학기 김기훈 Khkim@dblab.hannam.ac.kr. 목차. 쓰레드란 무엇인가 쓰레드 생성하기 다중 쓰레드 생성하기 임계영역 & 쓰레드의 문제점 뮤텍스 (mutex) 세마포어 (Semaphore) 쓰레드 기반 서버 구현. Thread Stack. Thread Stack. Thread Stack. Thread Stack. Global Variables. Heap.

morrie
Download Presentation

제 17 장 멀티쓰레드 기반의 서버구현

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. TCP/IP Socket Programming… 제 17장 멀티쓰레드 기반의 서버구현 데이터베이스 실험실 석사 2학기 김기훈 Khkim@dblab.hannam.ac.kr

  2. 목차 • 쓰레드란 무엇인가 • 쓰레드 생성하기 • 다중 쓰레드 생성하기 • 임계영역 & 쓰레드의 문제점 • 뮤텍스(mutex) • 세마포어(Semaphore) • 쓰레드 기반 서버 구현

  3. Thread Stack Thread Stack Thread Stack Thread Stack Global Variables Heap Global Variables Heap 쓰레드란 무엇인가[1] • 경량화 된 프로세스 • 프로세스와 마찬가지로 동시실행이 가능함 • 프로세스의 단점을 극복하기 위해 등장 • 프로세스와의 차이점 • 스텍을 제외한 나머지 메모리 공간을 공유 • 보다 간단한 컨텍스트 스위칭(context switching) • 일부메모리를 공유하므로 스레드간 통신이 편리 Process Process Thread 1 Thread Thread 1 Thread 1

  4. 쓰레드란 무엇인가[2] • 프로세스와 쓰레드

  5. #include <pthread.h> int pthread_create (pthead_t * thread, pthread_attr-t * attr, void * (* start_routine) (void *), void * arg ); 성공 시 0, 실패 시 이외의 값 리턴 쓰레드 생성하기[1] • 쓰레드를 생성하는 함수 • Pthread_create 함수 • thread : 생성된 쓰레드의 ID를 저장할 변수의 포인터를 인자로 전달 • Attr : 생성하고자 하는 쓰레드의 특성(attribute)을 설정할 때 사용, 일반적으로 Null을 전달 • Start_routine : 리턴타입과 인자가 void*인 함수를 가르키는 포인터 • Arg : 쓰레드에 의해 호출되는 함수에 전달하고자 하는 인자값을 넘겨줌

  6. 실행결과 Process 쓰레드 생성 Thread 종료 종료 쓰레드 생성하기[2] • thread1.c void *thread_function(void *arg); int main(int argc, char **argv){ int state; pthread_t t_id; void *t_return; state = pthread_create(&t_id, NULL, thread_function, NULL); if(state != 0){ puts("쓰레드 생성 오류"); exit(1); } printf("생성된 쓰레드 ID : %d \n", t_id); sleep(3); puts("main함수 종료"); return 0; } void * thread_function(void *arg) { int i; for(i=0; i<3; i++){ sleep(2); puts("쓰레드 실행 중"); } }

  7. #include <pthread.h> int pthread_join(pthead_t * th, void **thread_return); ); 성공 시 0, 실패 시 이외의 값 리턴 쓰레드 생성하기[3] • pthread_join 함수 • th : th에 인자로 들어오는 ID의 쓰레드가 종료할 때까지 실행 지연 • thread_return : 쓰레드가 종료 시 반환하는 값에 접근할 수 있는 2차원포인터

  8. Process 실행결과 쓰레드 생성 Thread JOIN 대기상태 RETURN 종료 종료 쓰레드 생성하기[4] • thread2.c int main(int argc, char **argv){ . . . void *t_return; state = pthread_create(&t_id, NULL, thread_function, NULL); . . . printf("생성된 쓰레드 ID : %d \n", t_id); /* 쓰레드 종료 시까지 main함수의 실행을 지연 */ state = pthread_join(t_id, &t_return); /* 리턴 값 저장 */ if(state !=0 ){ puts("쓰레드 Join 오류"); exit(1); } printf("main함수 종료, 쓰레드 리턴 %s", (char*)t_return); free(t_return); return 0; } void * thread_function(void *arg) { int i; char *p = (char*)malloc(20*sizeof(char)); strcpy(p, "쓰레드 종료됨 !\n"); for(i=0; i<3; i++){ sleep(2); puts("쓰레드 실행 중"); } return p; }

  9. 다중 쓰레드 생성하기[1] • 임계영역(Critical Section)과 쓰레드에 안전한 함수의 호출 • 임계영역 • 두개 이상의 쓰레드에 의해서 동시에 실행되면 안 되는 영역 • 쓰레드 관점에서 볼 때 함수의 종류 • 쓰레드 불안전한 함수(Thread-unsafe Function) • 단일 쓰레드 모델에서는 사용 가능함 함수이지만 다중 쓰레드 모델에서는 사용할 수 없는 함수(gethostbyname) • 쓰레드 안전한 함수(Thread-safe Function) • 다중 쓰레드 모델에서 사용 가능한 함수(gethostbyname_r) • 불안전한 함수를 안전한 함수로 변경 • 컴파일시 –D_REENTRANT를 옵션으로 넣어 주는 방식으로 매크로를 선언

  10. Process 쓰레드 생성 Thread 쓰레드 생성 Thread JOIN RETURN 종료 JOIN 실행결과 종료 종료 다중 쓰레드 생성하기[2] • Thread3.c void *thread_summation(void *arg); int sum=0; int sum1[]={1, 5}; int sum2[]={6, 10}; int main(int argc, char **argv){ pthread_t id_t1, id_t2; void *t_return; pthread_create(&id_t1, NULL, thread_summation, (void *)sum1); pthread_create(&id_t2, NULL, thread_summation, (void *)sum2); /* 쓰레드 종료 시까지 main함수의 실행을 지연 */ pthread_join(id_t1, &t_return); pthread_join(id_t2, &t_return); printf("main함수 종료, sum = %d \n", sum); return 0; } void * thread_summation(void *arg){ int start = ((int*)arg)[0]; int end = ((int*)arg)[1]; for(; start<=end; start++){ sum+=start; } }

  11. 실행결과 다중 쓰레드 생성하기[3] • Thread4.c #define NUMBER 10000 void *thread_increment(void *arg); int num=0; int main(int argc, char **argv){ int i; pthread_t thread_id[10]; void *t_return; for(i=0; i<10; i++) pthread_create(&thread_id[i], NULL, thread_increment, NULL); /* 생성한 모든 쓰레드 종료 시까지 main 함수의 실행을 지연 */ for(i=0; i<10; i++) pthread_join(thread_id[i], &t_return); printf("main함수 종료, num=%d \n", num); return 0; } void *thread_increment(void *arg){ int i; for(i=0; i<NUMBER; i++) num++; }

  12. 임계영역 & 쓰레드의 문제점[1] • 컴퓨터가 덧셈하는 원리 int i = 10 int j = 20 j+=i

  13. 임계영역 & 쓰레드의 문제점[2] • 두 개의 쓰레드에 의한 덧셈 원리 int i = 10 . . . . . i+=10

  14. 임계영역 & 쓰레드의 문제점[3] • 임계영역 • 두개 이상의 쓰레드에 의해서 공유되는 메모리 공간에 접근하는 코드영역 • 쓰레드의 동기화 • 공유된 메모리에 둘 이상의 쓰레드가 동시 접근하는 것을 막는 방법 • 둘 이상의 쓰레드 실행 순서를 컨트롤하는 방법 • 대표적인 동기화 기법 • 뮤텍스 • 세마포어

  15. 뮤텍스(Mutex)[1] • 뮤텍스 • Mutual Exclusion의 줄임말로 쓰레드들의 동시접근을 허용하지 않겠다는 의미 • Pthread_mutex_t 타입변수를 가르켜 흔히 뮤텍스라고 함 • 뮤텍스의 기본원리 • 임계영역에 들어갈 때 뮤텍스를 잠그고 들어감 • 임계영역을 빠져 나올 때 뮤텍스를 풀고 나옴 • 뮤텍스 조작함수 • 초기화 : pthread_mutex_init (pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr) • 잠금 : pthread_mutex_lock (pthread_mutex_t *mutex) • 잠금 해제 : pthread_mutex_unlock (pthread_mutex_t *mutex) • 소멸 : pthread_mutex_destroy (pthread_mutex_t *mutex)

  16. Thread B Thread A 임계영역 임계영역 Thread B 진입 Thread B Thread A 임계영역 Thread A 진입상태 뮤텍스(Mutex)[2] • 뮤텍스의 동기화 원리 2.Thread B 진입 pthread_mutex_lock 함수 호출 후 임계영역에 진입 [3] [1] Pthread_mutux_lock 함수 호출 후 대기상태 1. Pthread_mutex_unlock 함수 호출 후 임계 영역 탈출 [2]

  17. 뮤텍스(Mutex)[3] • mutex.c void *thread_increment(void *arg); char thread1[] = "A Thread"; char thread2[] = "B Thread"; pthread_mutex_t mutx; int number = 0; int main(int argc, char **argv){ pthread_t t1, t2; void *thread_result; int state; state = pthread_mutex_init(&mutx, NULL); if(state){ puts("뮤텍스 초기화 실패"); exit(1); } pthread_create(&t1, NULL, thread_increment, &thread1); pthread_create(&t2, NULL, thread_increment, &thread2); pthread_join(t1, &thread_result); pthread_join(t2, &thread_result); printf("최종 number : %d \n", number); pthread_mutex_destroy(&mutx); return 0; } void *thread_increment(void * arg) { int i; for(i=0; i<5; i++){ pthread_mutex_lock (&mutx); sleep(1); number++; printf ("실행 : %s, number : %d \n", (char*)arg, number); pthread_mutex_unlock (&mutx); } } 실행결과

  18. 세마포어(Semaphore)[1] • 세마포어 • sem_t 타입의 변수를 가르켜 흔히 세마포어라고 함 • 세마포어의 기본원리 • 정수를 가짐 • 정수 값이 0 이면 실행 불가능 • 세마포어가 1 이상이면 실행 가능 • 세마포어는 0 미만은 될수 없고 1 이상은 가능 • 세마포어 조작 함수 • 초기화 : sem_init (sem_t *sem, int pshared, unsigned int value) • 소멸 : sem_destory (sem_t *sem) • 증가 : sem_wait (sem_t *sem) • 감소 : sem_post (sem_t *sem)

  19. Thread A Thread A Thread B Data Data Thread B 세마포어(Semaphore)[2] • 세마포어의 동기화 원리 1.새로운 데이터를 저장 후 세마포어 하나 증가 2.새로운 데이터를 저장 후 세마포어 하나 증가 1. 세마포어가 현재 0이므로 sem_wait 함수 호출 시 대기 상태로 진입 3. 실행 상태로 돌아와 세마포어를 하나 감소 후 데이터 얻음. 2. 세마포어 값을 하나 감소 시킨 후, 데이터를 가져간다. [1] [2]

  20. 세마포어(Semaphore)[3] • semaphore.c void *thread_snd(void * arg){ int i; for(i=0; i<4; i++){ while(number != 0); sleep(1); number++; printf("실행 : %s, number : %d \n", (char*)arg, number); sem_post(&bin_sem); } } void *thread_rcv(void * arg){ int i; for(i=0; i<2; i++){ sem_wait(&bin_sem); number--; printf("실행 : %s, number : %d \n", (char*)arg, number); } } void *thread_snd(void *arg); void *thread_rcv(void *arg); sem_t bin_sem; int number = 0; char thread1[] = "A Thread"; char thread2[] = "B Thread"; char thread3[] = "C Thread"; int main(int argc, char **argv){ pthread_t t1, t2, t3; void *thread_result; int state; state = sem_init(&bin_sem, 0, 0); //bin_sem은 0으로 설정 if(state != 0){ puts("세마포어 초기화 실패"); exit(1); } pthread_create(&t1, NULL, thread_snd, &thread1); pthread_create(&t2, NULL, thread_rcv, &thread2); pthread_create(&t3, NULL, thread_rcv, &thread3); pthread_join(t1, &thread_result); pthread_join(t2, &thread_result); pthread_join(t3, &thread_result); printf("최종 number : %d \n", number); sem_destroy(&bin_sem); return 0; } 실행결과

  21. 세마포어(Semaphore)[4] • semaphore2.c void *thread_snd(void * arg){ int i; for(i=0; i<4; i++){ number++; printf("실행 : %s, number : %d \n", (char*)arg, number); sem_post(&bin_sem); sem_wait(&bin_sem2); } } void *thread_rcv(void * arg){ int i; for(i=0; i<2; i++){ sem_wait(&bin_sem); number--; printf("실행 : %s, number : %d \n", (char*)arg, number); sem_post(&bin_sem2); } } void *thread_snd(void *arg); void *thread_rcv(void *arg); sem_t bin_sem bin_sem2; int number = 0; char thread1[] = "A Thread"; char thread2[] = "B Thread"; char thread3[] = "C Thread"; int main(int argc, char **argv){ pthread_t t1, t2, t3; void *thread_result; int state; state = sem_init(&bin_sem, 0, 0); //bin_sem은 0으로 설정 if(state != 0){ puts("세마포어 초기화 실패"); exit(1); } pthread_create(&t1, NULL, thread_snd, &thread1); pthread_create(&t2, NULL, thread_rcv, &thread2); pthread_create(&t3, NULL, thread_rcv, &thread3); pthread_join(t1, &thread_result); pthread_join(t2, &thread_result); pthread_join(t3, &thread_result); printf("최종 number : %d \n", number); sem_destroy(&bin_sem); return 0; } 실행결과

  22. 쓰레드 기반 서버 구현[1] • chat_server.c #define BUFSIZE 100 void *clnt_connection(void *arg); void send_message(char *message, int len); void error_handling(char * message); int clnt_number = 0; int clnt_socks[10]; pthread_mutex_t mutx; int main(int argc, char **argv){ int serv_sock; int clnt_sock; struct sockaddr_in serv_addr; struct sockaddr_in clnt_addr; int clnt_addr_size; pthread_t thread; . . . . while(1){ clnt_addr_size = sizeof(clnt_addr); clnt_sock = accept(serv_sock, (struct sockaddr*) &clnt_addr, &clnt_addr_size); pthread_mutex_lock(&mutx); clnt_socks[clnt_number++] = clnt_sock; pthread_mutex_unlock(&mutx); pthread_create(&thread, NULL, clnt_connection, (void*)clnt_sock); printf("새로운 연결, 클라이언트 ip : %s \n", inet_ntoa(clnt_addr.sin_addr)); } return 0; } void *clnt_connection(void *arg){ int clnt_sock = (int)arg; int str_len = 0; char message[BUFSIZE]; int i; while((str_len = read(clnt_sock, message, sizeof(message))) != 0) send_message(message, str_len); pthread_mutex_lock(&mutx); for(i=0; i<clnt_number; i++){ /*클라이언트 연결 종료 시 */ if(clnt_sock == clnt_socks[i]){ for(; i<clnt_number-1; i++) clnt_socks[i] = clnt_socks[i+1]; break; } } clnt_number--; pthread_mutex_unlock(&mutx); close(clnt_sock); return 0; } void send_message(char * message, int len){ int i; pthread_mutex_lock(&mutx); for(i=0; i<clnt_number; i++) write(clnt_socks[i], message, len); pthread_mutex_unlock(&mutx); }

  23. 쓰레드 기반 서버 구현[2] • chat_client.c void *send_message(void *arg){ /*메시지 전송 쓰레드 실행 함수*/ int sock = (int)arg; char name_message[NAMESIZE+BUFSIZE]; while(1){ fgets(message, BUFSIZE, stdin); if(!strcmp(message, "q\n")){ /* 'q' 입력 시 종료 */ close(sock); exit(0); } sprintf(name_message, "%s %s", name, message); write(sock, name_message, strlen(name_message)); } } void *recv_message(void *arg){ int sock = (int)arg; char name_message[NAMESIZE+BUFSIZE]; int str_len; while(1){ str_len = read(sock, name_message, NAMESIZE+BUFSIZE-1); if(str_len == -1) return 1; name_message[str_len] = 0; fputs(name_message, stdout); } } #define BUFSIZE 100 #define NAMESIZE 20 void *send_message(void *arg); void *recv_message(void *arg); void error_handling(char * message); char name[NAMESIZE]="[Default]"; char message[BUFSIZE]; int main(int argc, char **argv){ int sock; struct sockaddr_in serv_addr; pthread_t snd_thread, rcv_thread; void *thread_result; . . . . sock=socket(PF_INET, SOCK_STREAM, 0); . . . . pthread_create(&snd_thread, NULL, send_message, (void*)sock); pthread_create(&rcv_thread, NULL, recv_message, (void*)sock); pthread_join(snd_thread, &thread_result); pthread_join(rcv_thread, &thread_result); close(sock); return 0; }

  24. 쓰레드 기반 서버 구현[3] • chat_server.c & chat_client.c 실행결과 server Client MR.Lee Client Thomas

  25. 참고문헌 • “TCP/IP 소켓 프로그래밍”, 윤성우 저 • “운영체제(Understanding operating Systems)”, 김희철, 박영민, 이금석, 조병호, 최의인 공역

  26. Q & A

More Related