제 17 장 멀티쓰레드 기반의 서버구현
DESCRIPTION
TCP/IP Socket Programming…. 제 17 장 멀티쓰레드 기반의 서버구현. 데이터베이스 실험실 석사 2 학기 김기훈 [email protected]. 목차. 쓰레드란 무엇인가 쓰레드 생성하기 다중 쓰레드 생성하기 임계영역 & 쓰레드의 문제점 뮤텍스 (mutex) 세마포어 (Semaphore) 쓰레드 기반 서버 구현. Thread Stack. Thread Stack. Thread Stack. Thread Stack. Global Variables. Heap. - PowerPoint PPT PresentationTRANSCRIPT
HANNAM HANNAM Univ.Univ.
제 제 1717 장 멀티쓰레드 기반의 서버구현장 멀티쓰레드 기반의 서버구현
데이터베이스 실험실데이터베이스 실험실석사 석사 22 학기 김기훈학기 김기훈
[email protected]@dblab.hannam.ac.kr
TCP/IP Socket TCP/IP Socket Programming…Programming…
2
Network Lab.Network Lab.
HANNAM HANNAM Univ.Univ.
TCP/IP Socket Programming…TCP/IP Socket Programming…
목차목차쓰레드란 무엇인가
쓰레드 생성하기
다중 쓰레드 생성하기
임계영역 & 쓰레드의 문제점
뮤텍스 (mutex)
세마포어 (Semaphore)
쓰레드 기반 서버 구현
3
Network Lab.Network Lab.
HANNAM HANNAM Univ.Univ.
TCP/IP Socket Programming…TCP/IP Socket Programming…
쓰레드란 무엇인가쓰레드란 무엇인가 [1][1]경량화 된 프로세스
■ 프로세스와 마찬가지로 동시실행이 가능함■ 프로세스의 단점을 극복하기 위해 등장
프로세스와의 차이점■ 스텍을 제외한 나머지 메모리 공간을 공유■ 보다 간단한 컨텍스트 스위칭 (context switching)■ 일부메모리를 공유하므로 스레드간 통신이 편리
Global Variables
Heap
Thread Stack
Thread
Process
Global Variables
Heap
Thread Stack
Thread 1
Process
Thread Stack
Thread 1
Thread Stack
Thread 1
4
Network Lab.Network Lab.
HANNAM HANNAM Univ.Univ.
TCP/IP Socket Programming…TCP/IP Socket Programming…
쓰레드란 무엇인가쓰레드란 무엇인가 [2][2]프로세스와 쓰레드
5
Network Lab.Network Lab.
HANNAM HANNAM Univ.Univ.
TCP/IP Socket Programming…TCP/IP Socket Programming…
쓰레드 생성하기쓰레드 생성하기 [1][1]쓰레드를 생성하는 함수
■ Pthread_create 함수
▶ thread : 생성된 쓰레드의 ID 를 저장할 변수의 포인터를 인자로 전달▶ Attr : 생성하고자 하는 쓰레드의 특성 (attribute) 을 설정할 때 사용 , 일반적으로 Null 을 전달▶ Start_routine : 리턴타입과 인자가 void* 인 함수를 가르키는 포인터▶ Arg : 쓰레드에 의해 호출되는 함수에 전달하고자 하는 인자값을 넘겨줌
#include <pthread.h>
int pthread_create (pthead_t * thread, pthread_attr-t * attr, void * (* start_routine) (void *), void * arg);
성공 시 0, 실패 시 이외의 값 리턴
6
Network Lab.Network Lab.
HANNAM HANNAM Univ.Univ.
TCP/IP Socket Programming…TCP/IP Socket Programming…
쓰레드 생성하기쓰레드 생성하기 [2][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(" 쓰레드 실행 중 ");
}}
실행결과
Process
Thread쓰레드 생성
종료 종료
7
Network Lab.Network Lab.
HANNAM HANNAM Univ.Univ.
TCP/IP Socket Programming…TCP/IP Socket Programming…
쓰레드 생성하기쓰레드 생성하기 [3][3]■ pthread_join 함수
▶ th : th 에 인자로 들어오는 ID 의 쓰레드가 종료할 때까지 실행 지연▶ thread_return : 쓰레드가 종료 시 반환하는 값에 접근할 수 있는 2
차원포인터
#include <pthread.h>
int pthread_join(pthead_t * th, void **thread_return););
성공 시 0, 실패 시 이외의 값 리턴
8
Network Lab.Network Lab.
HANNAM HANNAM Univ.Univ.
TCP/IP Socket Programming…TCP/IP Socket Programming…
쓰레드 생성하기쓰레드 생성하기 [4][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;}
실행결과
Process
Thread쓰레드 생성
종료
종료
JOIN
RETURN
대기상태
9
Network Lab.Network Lab.
HANNAM HANNAM Univ.Univ.
TCP/IP Socket Programming…TCP/IP Socket Programming…
다중 쓰레드 생성하기다중 쓰레드 생성하기 [1][1]임계영역 (Critical Section) 과 쓰레드에 안전한
함수의 호출■ 임계영역
▶ 두개 이상의 쓰레드에 의해서 동시에 실행되면 안 되는 영역■ 쓰레드 관점에서 볼 때 함수의 종류
▶ 쓰레드 불안전한 함수 (Thread-unsafe Function) 단일 쓰레드 모델에서는 사용 가능함 함수이지만 다중 쓰레드
모델에서는 사용할 수 없는 함수 (gethostbyname)▶ 쓰레드 안전한 함수 (Thread-safe Function)
다중 쓰레드 모델에서 사용 가능한 함수 (gethostbyname_r)
■ 불안전한 함수를 안전한 함수로 변경▶ 컴파일시 – D_REENTRANT 를 옵션으로 넣어 주는 방식으로 매크로를 선언
10
Network Lab.Network Lab.
HANNAM HANNAM Univ.Univ.
TCP/IP Socket Programming…TCP/IP Socket Programming…
다중 쓰레드 생성하기다중 쓰레드 생성하기 [2][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;
}}
Process
Thread쓰레드 생성
종료
종료
JOIN
RETURN
Thread쓰레드 생성
종료
JOIN
11
Network Lab.Network Lab.
HANNAM HANNAM Univ.Univ.
TCP/IP Socket Programming…TCP/IP Socket Programming…
다중 쓰레드 생성하기다중 쓰레드 생성하기 [3][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
Network Lab.Network Lab.
HANNAM HANNAM Univ.Univ.
TCP/IP Socket Programming…TCP/IP Socket Programming…
임계영역 임계영역 & & 쓰레드의 문제점쓰레드의 문제점 [1][1] 컴퓨터가 덧셈하는 원리
int i = 10int j = 20
j+=i
13
Network Lab.Network Lab.
HANNAM HANNAM Univ.Univ.
TCP/IP Socket Programming…TCP/IP Socket Programming…
임계영역 임계영역 & & 쓰레드의 문제점쓰레드의 문제점 [2][2]두 개의 쓰레드에 의한 덧셈 원리
int i = 10
. . . . .i+=10
14
Network Lab.Network Lab.
HANNAM HANNAM Univ.Univ.
TCP/IP Socket Programming…TCP/IP Socket Programming…
임계영역 임계영역 & & 쓰레드의 문제점쓰레드의 문제점 [3][3]임계영역
■ 두개 이상의 쓰레드에 의해서 공유되는 메모리 공간에 접근하는 코드영역
쓰레드의 동기화■ 공유된 메모리에 둘 이상의 쓰레드가 동시 접근하는 것을 막는
방법 ■ 둘 이상의 쓰레드 실행 순서를 컨트롤하는 방법
대표적인 동기화 기법■ 뮤텍스■ 세마포어
15
Network Lab.Network Lab.
HANNAM HANNAM Univ.Univ.
TCP/IP Socket Programming…TCP/IP Socket Programming…
뮤텍스뮤텍스 (Mutex)[1](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
Network Lab.Network Lab.
HANNAM HANNAM Univ.Univ.
TCP/IP Socket Programming…TCP/IP Socket Programming…
뮤텍스뮤텍스 (Mutex)[2](Mutex)[2]뮤텍스의 동기화 원리
Thread A
임계영역
Thread B
Thread B
임계영역
Thread A 진입상태
임계영역
Thread B 진입
pthread_mutex_lock 함수 호출 후
임계영역에 진입
Pthread_mutux_lock 함수 호출 후 대기상태
Thread A
2.Thread B 진입
1. Pthread_mutex_unlock 함수 호출 후 임계 영역 탈출
[1]
[2]
[3]
17
Network Lab.Network Lab.
HANNAM HANNAM Univ.Univ.
TCP/IP Socket Programming…TCP/IP Socket Programming…
뮤텍스뮤텍스 (Mutex)[3](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
Network Lab.Network Lab.
HANNAM HANNAM Univ.Univ.
TCP/IP Socket Programming…TCP/IP Socket Programming…
세마포어세마포어 (Semaphore)[1](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
Network Lab.Network Lab.
HANNAM HANNAM Univ.Univ.
TCP/IP Socket Programming…TCP/IP Socket Programming…
세마포어세마포어 (Semaphore)[2](Semaphore)[2]세마포어의 동기화 원리
Thread A
Data
Thread A
1.새로운 데이터를저장 후 세마포어하나 증가
Thread B
Thread B
2. 세마포어 값을하나 감소 시킨 후 ,데이터를 가져간
다 .
Data
2.새로운 데이터를저장 후 세마포어하나 증가
3. 실행 상태로 돌아와세마포어를 하나 감소후 데이터 얻음 .
1. 세마포어가 현재 0 이므로 sem_wait 함수 호출 시대기 상태로 진입
[1] [2]
20
Network Lab.Network Lab.
HANNAM HANNAM Univ.Univ.
TCP/IP Socket Programming…TCP/IP Socket Programming…
세마포어세마포어 (Semaphore)[3](Semaphore)[3]semaphore.c
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;}
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); }}
실행결과
21
Network Lab.Network Lab.
HANNAM HANNAM Univ.Univ.
TCP/IP Socket Programming…TCP/IP Socket Programming…
세마포어세마포어 (Semaphore)[4](Semaphore)[4]semaphore2.c
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;}
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);
}}
실행결과
22
Network Lab.Network Lab.
HANNAM HANNAM Univ.Univ.
TCP/IP Socket Programming…TCP/IP Socket Programming…
쓰레드 기반 서버 구현쓰레드 기반 서버 구현 [1][1]chat_server.c
#define BUFSIZE 100void *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
Network Lab.Network Lab.
HANNAM HANNAM Univ.Univ.
TCP/IP Socket Programming…TCP/IP Socket Programming…
쓰레드 기반 서버 구현쓰레드 기반 서버 구현 [2][2]chat_client.c
#define BUFSIZE 100#define NAMESIZE 20void *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;}
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); }}
24
Network Lab.Network Lab.
HANNAM HANNAM Univ.Univ.
TCP/IP Socket Programming…TCP/IP Socket Programming…
쓰레드 기반 서버 구현쓰레드 기반 서버 구현 [3][3]chat_server.c & chat_client.c
server
Client MR.Lee
ClientThoma
s
실행결과
25
Network Lab.Network Lab.
HANNAM HANNAM Univ.Univ.
TCP/IP Socket Programming…TCP/IP Socket Programming…
참고문헌참고문헌 “TCP/IP 소켓 프로그래밍” , 윤성우 저 “ 운영체제 (Understanding operating Systems)”,
김희철 , 박영민 , 이금석 , 조병호 , 최의인 공역
26
Network Lab.Network Lab.
HANNAM HANNAM Univ.Univ.
TCP/IP Socket Programming…TCP/IP Socket Programming…
Q & AQ & A