반응형
공동구역?
프로세스는 여러 가지 쓰레드로 나뉠 수 있다. 여러 개로 나뉘어지는 쓰레드 중에서 자원을 공유할 수 있게 되는데,
이에 대한 처리가 너무너무너무너무 중요하다.
int a = 2; while(1) {} |
int b = 3; while(1) {} |
int c = 4; while(1) {} |
쓰레드1 |
쓰레드2 |
쓰레드3 |
프로세스(공유 자원) |
Critical Area
= 임계구역, 각각의 쓰레드가 공통으로 사용하는 프로세스의 자원
○ 이에 대한 동기화 작업이 이뤄지지 않으면, 치명적인 결함으로 이루어질 수 있다.
1. Mutual Exclusion(상호 베타) : 데이터는 반드시 한 순간에 하나의 쓰레드만 이용해야 한다.
2. Progress(진행) : 쓰레드 사이에서 누가 먼저 접근할 것인가를 정해진 시간내에 결정해야 한다.
3. Bounded Waiting(유한한 대기) : 쓰레드가 임계구역에 접근할 때 정해진 시간 내에 접근해야 한다.
Ex) Dekker 알고리즘, Peterson 알고리즘
다음은 Peterson 알고리즘의 예시이다.
● 조건들
1 2 3 4 5 6 | bool flag[2] // 불린 배열(Boolean array) int turn // 정수형 변수 flag[0] = false // false은 임계 구역 사용을 원하지 않음을 뜻함. flag[1] = true turn = 0 // 0 은 0번 프로세스를 가리킴, 1은 1번 프로세스를 가리킴 | cs |
○ Process (0)
1 2 3 4 5 6 7 8 9 10 11 | flag[0] = true // 임계 구역 사용을 원함 turn = 1 // 1번 프로세스에게 차례가 감 while( flag[1] && turn == 1 ) { // flag[1] 이 turn[1] 을 가지고 있으므로 //현재 사용중임 // 임계 구역이 사용 가능한지 계속 확인 }// 임계 구역 ... // 임계 구역의 끝 flag[0] = false | cs |
○ Process ①
1 2 3 4 5 6 7 8 9 | flag[1] = true turn = 0 while( flag[0] && turn == 0 ) { // 임계 구역이 사용 가능한지 계속 확인 }// 임계 구역 ... // 임계 구역의 끝 flag[1] = false | cs |
우리는 기본적인 동기화에 대한 중요성을 알았으니 다음은 방법을 알아보자.
Mutex
뮤텍스의 핵심은, Lock / Unlock 방식 2가지 case로 분류할 수 있다는 것이다.
Lock을 하게 되면 쓰레드의 실행을 잠금하고, Unlock을 하게 되면 잠금된 상태를 해제하여 작동하게 하는 방식이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | #include <pthread.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> // 뮤텍스 객체 선언 pthread_mutex_t mutex_lock; int g_count = 0; // 쓰레드 공유자원. void *t_function(void *data) { int i; char* thread_name = (char*)data; pthread_mutex_lock(&mutex_lock); // critical section for (i = 0; i < 3; i++) { printf("%s COUNT %d\n", thread_name, g_count); g_count++; // 쓰레드 공유자원 sleep(1); } pthread_mutex_unlock(&mutex_lock); } int main() { pthread_t p_thread1, p_thread2; int status; // 뮤텍스 객체 초기화, 기본 특성으로 초기화 했음 pthread_mutex_init(&mutex_lock, NULL); pthread_create(&p_thread1, NULL, t_function, (void *)"Thread1"); pthread_create(&p_thread2, NULL, t_function, (void *)"Thread2"); pthread_join(p_thread1, (void *)&status); pthread_join(p_thread2, (void *)&status); } |
# compile :: gcc -o name name.c -lpthread
[ 실행 결과 ]
Thread2 COUNT0
Thread2 COUNT1
Thread2 COUNT2
Thread1 COUNT3
Thread1 COUNT4
Thread1 COUNT5
Semaphore
세마포는 쓰레드간의 시그널을 주고받기 위해 사용되는 정수 값이다. 리소스의 상태를 나타내는 카운터이다.
단순화하기 위하여 딱 3가지 연산만 가능하다.
1. Initialize : 세마포어 초기화
2. decrement : 쓰레드를 블록시킬 수 있다.
3. increment : 블록시켰던 쓰레드를 깨울 수 있다.
알기 쉽게 바로 예제로 가보도록 하자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | #include <semaphore.h> #include <pthread.h> #include <stdio.h> sem_t semaphore; int num = 2; void *threadF1(void *arg) { int i; if(num != 0) { num--; sem_wait(&semaphore); printf("스레드 1이 화장실을 이용하기 시작합니다.\n"); // 화장실 이용하는 시간이라 가정 for(i = 0 ; i < 1000000 ; i++){} num++; sem_post(&semaphore); printf("스레드 1이 화장실 이용을 마쳤습니다.\n"); } else if(num == 0) { printf("화장실이 꽉 차 스레드 1이 기다리고 있습니다.\n"); while(num == 0){} num++; sem_post(&semaphore); printf("스레드 1이 화장실 이용을 하고 마쳤습니다.\n"); } return NULL; } void *threadF2(void *arg) { int i; if(num != 0) { num--; sem_wait(&semaphore); printf("스레드 2가 화장실을 이용하기 시작합니다.\n"); // 화장실 이용하는 시간이라 가정 for(i = 0 ; i < 1000000 ; i++){} num++; sem_post(&semaphore); printf("스레드 2가 화장실 이용을 마쳤습니다.\n"); } else if(num == 0) { printf("화장실이 꽉 차 스레드 2가 기다리고 있습니다.\n"); while(num == 0){} num++; sem_post(&semaphore); printf("스레드 2가 화장실 이용을 하고 마쳤습니다.\n"); } return NULL; } void *threadF3(void *arg) { int i; if(num != 0) { num--; sem_wait(&semaphore); printf("스레드 3이 화장실을 이용하기 시작합니다.\n"); // 화장실 이용하는 시간이라 가정 for(i = 0 ; i < 1000000 ; i++){} num++; sem_post(&semaphore); printf("스레드 3이 화장실 이용을 마쳤습니다.\n"); } else if(num == 0) { printf("화장실이 꽉 차 스레드 3이 기다리고 있습니다.\n"); while(num == 0){} num++; sem_post(&semaphore); printf("스레드 3이 화장실 이용을 하고 마쳤습니다.\n"); } return NULL; } int main() { pthread_t thread1, thread2, thread3; sem_init(&semaphore, 0, 2); // return :: 0 -> success , others -> fail printf("Semaphore test Start!\n"); // 스레드 생성 pthread_create(&thread1, NULL, threadF1, NULL); pthread_create(&thread2, NULL, threadF2, NULL); pthread_create(&thread3, NULL, threadF3, NULL); // 스레드 조인 pthread_join(thread1, NULL); pthread_join(thread2, NULL); pthread_join(thread3, NULL); printf("모든 스레드가 화장실 이용을 끝냈습니다.\n"); // 세마포어 객체 소멸 sem_destroy(&semaphore); return 0; } | cs |
# compile :: gcc -o name name.c -lpthread
[실행 결과]
1. 순조롭게 화장실을 왔다 갔다 하는 모습이다.
2. 화장실이 2개밖에 없으므로 모두 사용시 스레드 1이 블록되는 모습을 볼 수 있다.
세마포와 뮤텍스의 차이?
- 뮤텍스는 동기화 대상이 하나밖에 지정되지 않지만, 세마포는 여러 대상을 상대로 동기화를 진행할 수 있다.
- 세마포는 소유하지 않고 있는 스레드가 세마포어를 해제할 수 있지만, 뮤텍스는 반드시 뮤텍스를 소유해야 해제할 수 있다.
∴ 그래서,, 세마포는 뮤텍스가 될 수 있지만, 뮤텍스는 세마포가 될 수 없다.
반응형
'Developer > OS' 카테고리의 다른 글
7. Virtual Memory - 가상메모리 (1) | 2018.12.20 |
---|---|
6. Memory Fragmentation - 메모리 단편화 (0) | 2018.12.18 |
5. Monitor & Deadlock - 모니터, 데드락 (0) | 2018.12.17 |
2. Process - 프로세스 (0) | 2018.12.11 |
1. What is Operation System? 운영체제란? (0) | 2018.12.11 |