본문 바로가기

대학교시절

운영체제 과제 #2 (뮤텍스와 세마포어 사용하기)

Producer/Consumer 문제를 해결하는 프로그램을 작성 하라.


- 자료를 저장할 큐를 만들고 Pthread를 사용하여 Producer/Consumer thread를 생성한다. Producer thread는 큐의 자료를 1로 계속 채우고, Consumer thread는 큐의 자료를 소모하여 0으로 만든다. 이때, Consumer thread는 큐의 가장 오래된 자료부터 소모하고, Producer thread는 큐의 가장 최근에 자료를 생성했던 다음 빈자리부터 자료를 채운다. (최초의 자료 생성시 큐의 가장 왼쪽부터 생성하고, 큐의 가장 오른쪽에 도달 시 큐의 가장 왼쪽으로 이동한다.) Consumer thread가 자료를 소모하거나 Producer thread가 자료를 생성하는 것을 각각 1번의 Request라고 취급한다. 프로그램을 시작할 때, 큐의 사이즈, 실행할 Request의 수, Producer thread의 수, Consumer thread의 수를 인자로 입력 받을 수 있고 입력방법은 다음과 같다. 그리고 입력 받은 만큼의 수만큼 Request가 일어났을 때, 프로그램을 종료한다.


$hw02 -q “큐의 사이즈” -r “Request의 수” -p “Producer thread의 수” –c “Consumer thread의 수”


예)$hw02 –q 100 –r 1000 –p 2 –c 5


프로그램 실행 시 위와 같이 입력 받았을 때, 100개의 자료를 저장할 수 있는 큐를 만들고, 2개의 Producer thread가 자료를 생성, 5개의 Consumer thread가 자료를 소모하고, 총 1000개의 Request가 수행 되었을 때 프로그램을 종료한다.


인자들 중에 제한범위를 벗어난 것이 있으면 오류 메시지를 출력하고 바로 프로그램을 종료하고, 만약 프로그램을 실행 시 인자를 생략한다면 default값으로 프로그램을 실행한다.


 

 제한범위

 디폴트 값 

 -q

 1~200

 100 

 -r

 1`~50000

 5000 

 -p

 1~10

 3 

 -c

 1~10

 3 


프로그램이 실행되면 Request가 수행 될 때마다 Request의 번호와 큐의 내용을 한 줄로 출력한다.

출력의 예시는 다음과 같다.




Request를 모두 수행하고 난 후에 아래와 같이 각thread들이 수행한 request 수를 출력하고 프로그램을 종료한다.






과제 1 Mutex를 이용한 동기화 – 위의 Producer/Consumer 문제를 mutex를 이용하여 동기화 시킨다.


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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
/*
프로그램 작성자 : americano@korea.ac.kr
개발 날짜 : 2012-11-16
*/
int queue[200]; // MAX SIZE = 200
int box = 0;    //소비, 생산하는 물건
int r_size  = 5000; //request 의 기본 크기는 5000
int q_size  = 100;  //큐의 기본 크기는 100;
int request = 0;    //request를 실행한 횟수
int produce = 0;    //생산한 횟수
int consume = 0;    //소비한 횟수
int produce_num[10]; //생산 쓰레드 번호 배열
int consume_num[10]; //소비 쓰레드 번호 배열
pthread_mutex_t  mutex = PTHREAD_MUTEX_INITIALIZER;
 
int set(int what, int id){
    int i = 0;
    pthread_mutex_lock(&mutex);
    if(request>=r_size){ //명령어를 모두 실행하면 쓰레드종료
        pthread_mutex_unlock(&mutex);
        return -1;
    }
    if(what == 1){
        if(box>=q_size){ //더이상 생산할 수 없으면 함수종료
            pthread_mutex_unlock(&mutex);
            return 0;
        }
        queue[produce%q_size] = 1;
        produce++;
        box++;
        request++;
        produce_num[id] += 1;
    }      
    else{
        if(box<=0){ //더이상 소비 할수 없으면 함수 종료
            pthread_mutex_unlock(&mutex);
            return 0;
        }
        queue[consume%q_size] = 0;
        consume++;
        box--;
        request++;
        consume_num[id] += 1;
    }
    //결과 출력
    printf("%6d ", request);
    for(i = 0; i<q_size; i++)
        printf("%d",queue[i]);
    printf("\n");
    pthread_mutex_unlock(&mutex);
    return 0;
}
//생산자 쓰레드
void *produce_thread(void *tid){
    int id = *((int *)tid);
    int i;
    while(1){
        if(set(1,id)==-1)
            pthread_exit(NULL);
    }
}
//소비자 쓰레드
void *consume_thread(void *tid){
    int id = *((int *)tid);
    int i;
    while(1){
        if(set(0,id)==-1)
            pthread_exit(NULL);
    }
}
 
int main(int argc, char* argv[]){
    int i;
    char* q_str = "-q";
    char* r_str = "-r";
    char* p_str = "-p"; int p_size = 3;
    char* c_str = "-c"; int c_size = 3;
    int status  = 0;
    int p_num[10];
    int c_num[10];
    pthread_t thread_p[10];
    pthread_t thread_c[10];
    //init setting
    for(i = 0; i<argc; i++){
        if(strcmp(argv[i],q_str)==0)
            q_size = atoi(argv[i+1]);
        if(strcmp(argv[i],r_str)==0)
            r_size = atoi(argv[i+1]);
        if(strcmp(argv[i],p_str)==0)
            p_size = atoi(argv[i+1]);
        if(strcmp(argv[i],c_str)==0)
            c_size = atoi(argv[i+1]);
    }
    if(q_size<1 || q_size>200){
        printf("q_size is not correct\n");
        return -1;
    }
    if(r_size<1 || r_size>50000){
        printf("r_size is not correct\n");
        return -1;
    }
    if(p_size<1 || p_size>10){
        printf("p_size is not correct\n");
        return -1;
    }
    if(c_size<1 || c_size>10){
        printf("c_size is not correct\n");
        return -1;
    }
    // 쓰레드 번호를 위해 숫자 초기화 작업
    for(i = 0; i<p_size; i++)
        p_num[i] = i;
    for(i = 0; i<c_size; i++)
        c_num[i] = i;
    //생산자 쓰레드 생성
    for(i = 0; i<p_size; i++){
        status = pthread_create(&thread_p[i], NULL,  produce_thread,(void*) (p_num+i));
        if(status != 0)
            printf("produce : pthread_creat returned error code : %d\n",status);
    }
    //소비자 쓰레드 생성
    for(i = 0; i<c_size; i++){
        status = pthread_create(&thread_c[i], NULL, consume_thread, (void*) (c_num+i));
        if(status != 0)
            printf("consume : pthread_creat returned error code : %d\n",status);
    }
     
    //생산자가 모두 끝날 때 까지 기다림
    for(i = 0; i<p_size; i++){
        pthread_join(thread_p[i], (void**)&status);
    }
    //소비자가 모두 끝날 때 까지 기다림
    for(i = 0; i<c_size; i++){
        pthread_join(thread_c[i], (void**)&status);
    }
    // 생산자 쓰레드의 명령어 실행 횟수 출력
    for(i = 0; i<p_size; i++){
        printf("producer %d:%d\n",i,produce_num[i]);
    }
    printf("\n");
    //소비자 쓰레드의 명령어 실행 횟수 출력
    for(i = 0; i<c_size; i++){
        printf("consumer %d:%d\n",i,consume_num[i]);
    }
     
     
    return 0;
}


과제 2.Semaphore를 이용한 동기화 - 위의 Producer/Consumer 문제를 Semaphore를 이용하여 동기화 시킨다.


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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <semaphore.h>
/*
프로그램 작성자 : americano@korea.ac.kr
개발 날짜 : 2012-11-16
*/
int queue[200]; // MAX SIZE = 200
int box = 0; // 생산,  소비할 매개체
int r_size  = 5000; //명령어의 기본크기는 5000
int q_size  = 100;  //큐의 기본크기는 100
int request = 0;
int produce = 0;
int consume = 0;
int produce_num[10]; //생산자쓰레드의 번호를 저장할 배열
int consume_num[10]; //소비자쓰레드의 번호를 저장할 배열
//세마포어변수들
sem_t full;
sem_t empty;
sem_t mutex;
int set(int what, int id){
    int i = 0;
    if(what == 1){ // 생산자 쓰레드
        box++;
        sem_wait(&empty);
        sem_wait(&mutex);
        if(request>=r_size){ //명령어가 모두 실행되면 종료
            sem_post(&mutex);
            sem_post(&full);
            return -1;
        }
        queue[produce%q_size] = 1;
        produce++;
        request++;
        produce_num[id] += 1;
        printf("%6d ", request); //결과출력
        for(i = 0; i<q_size; i++)
            printf("%d",queue[i]);
        printf("\n");
        sem_post(&mutex);
        sem_post(&full);
        return 0;
    }      
    else{ // 소비자 쓰레드
        sem_wait(&full);
        sem_wait(&mutex);//명령어가 모두 실행되면 쓰레드 종료
        if(request>= r_size){
            sem_post(&mutex);
            sem_post(&empty);
            return -1;
        }
        queue[consume%q_size] = 0;
        consume++;
        request++;
        consume_num[id] += 1;
        printf("%6d ", request); // 결과출력
        for(i = 0; i<q_size; i++)
            printf("%d",queue[i]);
        printf("\n");
        sem_post(&mutex);
        sem_post(&empty);
        box--;
        return 0;
    }
}
void *produce_thread(void *tid){ // 생산자 쓰레드
    int id = *((int *)tid);
    int i;
    while(1){
        if(set(1,id)==-1)
            pthread_exit(NULL);
    }
}
 
void *consume_thread(void *tid){
    int id = *((int *)tid); //소비자 쓰레드
    int i;
    while(1){
        if(set(0,id)==-1)
            pthread_exit(NULL);
    }
}
 
int main(int argc, char* argv[]){
    int i;
    int state;
    char* q_str = "-q";
    char* r_str = "-r";
    char* p_str = "-p"; int p_size = 3;
    char* c_str = "-c"; int c_size = 3;
    int status  = 0;
    int p_num[10]; //쓰레드의 번호를 저장할 배열
    int c_num[10];
    pthread_t thread_p[10];
    pthread_t thread_c[10];
    //init setting
    for(i = 0; i<argc; i++){
        if(strcmp(argv[i],q_str)==0)
            q_size = atoi(argv[i+1]);
        if(strcmp(argv[i],r_str)==0)
            r_size = atoi(argv[i+1]);
        if(strcmp(argv[i],p_str)==0)
            p_size = atoi(argv[i+1]);
        if(strcmp(argv[i],c_str)==0)
            c_size = atoi(argv[i+1]);
    }
    if(q_size<1 || q_size>200){
        printf("q_size is not correct\n");
        return -1;
    }
    if(r_size<1 || r_size>50000){
        printf("r_size is not correct\n");
        return -1;
    }
    if(p_size<1 || p_size>10){
        printf("p_size is not correct\n");
        return -1;
    }
    if(c_size<1 || c_size>10){
        printf("c_size is not correct\n");
        return -1;
    }
    for(i = 0; i<p_size; i++)
        p_num[i] = i;
    for(i = 0; i<c_size; i++)
        c_num[i] = i;
    if(sem_init(&full,0,0)!=0){
        printf("sem_init Error\n");
        return 0;
    }
    if(sem_init(&empty,0,q_size)!=0){
        printf("sem_init Error\n");
        return 0;
    }
    if(sem_init(&mutex,0,1)!=0){
        printf("sem_init Error\n");
        return 0;
    }
    //생산자 쓰레드 생성
    for(i = 0; i<p_size; i++){
        status = pthread_create(&thread_p[i], NULL,  produce_thread,(void*) (p_num+i));
        if(status != 0)
            printf("produce : pthread_creat returned error code : %d\n",status);
    }
    //소비자 쓰레드 생성
    for(i = 0; i<c_size; i++){
        status = pthread_create(&thread_c[i], NULL, consume_thread, (void*) (c_num+i));
        if(status != 0)
            printf("consume : pthread_creat returned error code : %d\n",status);
    }
    //쓰레드가 끝날때까지 기다림
     
    for(i = 0; i<p_size; i++){
        pthread_join(thread_p[i], (void**)&status);
    }
     
    for(i = 0; i<c_size; i++){
        pthread_join(thread_c[i], (void**)&status);
    }
    //생산자가 모두 끝나면 결과를 출력
    for(i = 0; i<p_size; i++){
        printf("producer %d:%d\n",i,produce_num[i]);
    }
    printf("\n");
    //소비자가 모두 끝나면 결과를 출력
    for(i = 0; i<c_size; i++){
        printf("consumer %d:%d\n",i,consume_num[i]);
    }
    sem_destroy(&full);
    sem_destroy(&empty);
    sem_destroy(&mutex);
     
    return 0;
}



Appendix


*자세한 내용은 man 페이지를 참조


1) int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);


2) int pthread_join(pthread_t thread, void **retval);


3) 

int pthread_mutex_lock(pthread_mutex_t *mutex); 

int pthread_mutex_unlock(pthread_mutex_t *mutex);


4) int sem_init(sem_t *sem, int pshared, unsigned int value);


5) int sem_destroy(sem_t *sem);


6) int sem_wait(sem_t *sem);


7) int sem_post(sem_t *sem);


*기타 참고사항


Pthread API를 사용하는 프로그램은 컴파일시 –lpthread 옵션을 사용하여 libpthread 라이브러리를 링크해야 오류가 나지 않는다.


사용 예) $gcc –o hw02 hw02.c -lpthread