-
Notifications
You must be signed in to change notification settings - Fork 0
/
tp2_threads.c
235 lines (199 loc) · 9.58 KB
/
tp2_threads.c
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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
/*
* Sistema: TP02 - Questão 2 de BCC264 - Sistemas Operacionais.
* Autor: Josué Villa Real
*
* Descrição: Este programa foi desenvolvido para a disciplina de Sistemas Operacionais
* lecionada pelo professor Carlos Frederico na Universidade Federal de Ouro Preto. O
* programa tem como principal objetivo a impressão de um sistema de threads que
* visam mostrar como são feitos threads em um sistema fechado e como manupular os dados
* entre elas.
*
* Principais pontos: Foi solicitado no enunciado do trabalho que explicassemos alguns pontos, vou explica-los
* tanto aqui quanto em meu video para não deixar nada passar.
*
* 1 - Race Condition: O meu codigo apresenta sim race condition pois a a variável compartilhada balance é
* acessada e modificada pelas threads de incremento e decremento sem qualquer mecanismo de sincronização,
* o que pode levar a resultados inconsistentes. Para resolver isso eu precisaria usar um mecanismo de
* mutex ou semaphoros que foram apresentados em aula, com mutex trancaria a variavel balance para
* "protege-la" das demais threads do sistema, assim evitando que uma thread acesse a variavel enquanto
* outra esta modificando a mesma. Como acho que o intuito desse trabalho era só mostrar a criação de threads
* e como elas funcionam, não implementei nenhum mecanismo de sincronização.
*
* 2 - Principais diferenças entre threads e processos: Notei durante a implentação do trabalho o porque
* de os forks levarem o nome de processos "pesados", notei logo apos de implementar as threads, pois as
* mesmas são uma forma mais "leve" de implementar o paralelismo. Falando muito por cima, as threads são
* diferentes em relação aos forks pois elas compartilham o mesmo espaço de endereçamento, enquanto que
* os forks tem um espaço de endereçamento diferente, tornando mais dificil a comunicação entre as
* "entidades" do programa. O custo de criação das threads também é menor pois exige menos recursos do sistema
* mas no caso dos forks é necessario duplicar todo o processo, o que exige mais recursos do sistema.
* No mais, notei que ao criar uma thread, são criadas entidades que compartilham do mesmo processo, então
* o compartilhamento de memoria é mais independente (por isso não implementei o pipe aqui), enquanto que
* no fork, são criados tres processos diferentes que são cópias independentes, então a troca de informações
* entre eles é mais dificil.
*
* Acho que os forks são mais utilizados em sistemas que precisam de mais segurança, pois como os processos
* não se comunicam, é mais dificil de um processo "roubar" informações de outro, enquanto que as threads
* são mais utilizadas em sistemas que precisam de mais velocidade, pois como elas compartilham o mesmo
* espaço de endereçamento, a troca de informações é mais rapida. Isso se remete a escalabilidade, onde
* as threads podem ser escalonadas em sistemas com suporte de multthreading, possibilitando extrair mais
* desempenho do sistema. E os forks são mais visados em sistemas fechados que trabalham com processos
* independentes e isolados.
*
* 3 - Diferenças entre pthread_join e pthread_kill e pthread_exit: A pthread_join é utilizada para esperar
* que uma thread termine sua execução, enquanto que a pthread_kill é utilizada para enviar um sinal para
* uma thread, geralmente se usa o sinal SIGKILL para matar uma thread. Já a pthread_exit é utilizada para
* terminar a execução de uma thread, ela é mais utilizada quando a thread termina sua execução de forma
* inesperada, como por exemplo, quando ocorre um erro de segmentação ou só para finalizar a thread mesmo.
*
* No geral esse programa serve para mostrar mais a criação de threads.
*
* Uso: Para compilar esse programa, basta digitar:
* gcc -o "nome preferivel do executavel" tp2_threads.c
* depois disso, digite:
* ./nome_do_executavel
*
* */
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <sys/syscall.h>
#define ADD_OPERATION '+'
#define SUBTRACT_OPERATION '-'
#define PRINT_OPERATION 's'
#define EXIT_OPERATION 'k'
#define RESTART_OPERATION 'r'
int balance = 0;
char operation;
struct thread_data {
pthread_t thread;
};
void main_menu();
/* Funções para cada operação de cada thread*/
void *increment_function(void *arg);
void *decrement_function(void *arg);
void *print_function(void *arg);
void *restart_function(void *arg);
int main() {
main_menu();
pthread_t increment_thread;
pthread_t decrement_thread;
pthread_t print_thread;
pthread_t restart_thread;
// Criando as structs para cada thread.
struct thread_data *thread_of_print = (struct thread_data *) malloc(sizeof(struct thread_data));
struct thread_data *thread_of_increment = (struct thread_data *) malloc(sizeof(struct thread_data));
struct thread_data *thread_of_decrement = (struct thread_data *) malloc(sizeof(struct thread_data));
// Atribuindo as threads para cada struct.
thread_of_print->thread = print_thread;
thread_of_increment->thread = increment_thread;
thread_of_decrement->thread = decrement_thread;
// Criando as threads.
pthread_create(&print_thread, NULL, print_function, (void *) thread_of_print);
pthread_create(&increment_thread, NULL, increment_function, (void *) thread_of_increment);
pthread_create(&decrement_thread, NULL, decrement_function, (void *) thread_of_decrement);
do {
//Lendo qual a operação a ser realizada.
scanf(" %c", &operation);
//Discartando caracteres "extras" do buffer.
while (getchar() != '\n') {}
//Se for a opção de sair, mata as threads.
if (operation == EXIT_OPERATION) {
pthread_kill(increment_thread, 0);
pthread_kill(decrement_thread, 0);
pthread_kill(print_thread, 0);
printf("\n");
printf("==============================================\n");
printf("Terminating Threads...\n");
printf("\n");
}
//Se for a opção de reiniciar o sistema, mata as threads e cria novas.
else if (operation == RESTART_OPERATION) {
pthread_join(increment_thread, NULL);
pthread_kill(increment_thread, 0);
pthread_join(decrement_thread, NULL);
pthread_kill(decrement_thread, 0);
pthread_join(print_thread, NULL);
pthread_kill(print_thread, 0);
pthread_create(&restart_thread, NULL, restart_function, NULL);
pthread_create(&print_thread, NULL, print_function, (void *) thread_of_print);
pthread_create(&increment_thread, NULL, increment_function, (void *) thread_of_increment);
pthread_create(&decrement_thread, NULL, decrement_function, (void *) thread_of_decrement);
pthread_join(restart_thread, NULL);
main_menu();
}
} while (operation != EXIT_OPERATION);
return 0;
}
// Menu main_menu
void main_menu() {
printf("\n");
printf("Press %c to add 1000 UD\n", ADD_OPERATION);
printf("Press %c to subtract 1000 UD\n", SUBTRACT_OPERATION);
printf("Press %c to print the balance value\n", PRINT_OPERATION);
printf("Press %c to exit the program\n", EXIT_OPERATION);
printf("Press %c to restart all operations\n", RESTART_OPERATION);
printf("\n");
}
//Função de icremento.
void *increment_function(void *arg) {
//Loop infinito para a thread ficar sempre ativa, esperando a operação ser chamada.
do {
if (operation == ADD_OPERATION) {
printf("==============================================\n");
printf("\nAddition thread TID: %ld\n", syscall(__NR_gettid));
printf("\n");
balance += 1000;
operation = 0;
}
//Se a operação for de saída ou reiniciar, a thread é finalizada.
else if (operation == EXIT_OPERATION || operation == RESTART_OPERATION) {
pthread_exit(0);
}
} while (1);
}
// Função de decremento.
void *decrement_function(void *arg) {
// Loop infinito para a thread ficar sempre ativa, esperando a operação ser chamada.
do {
if (operation == SUBTRACT_OPERATION) {
printf("==============================================\n");
printf("\nSubtraction thread TID: %ld\n", syscall(__NR_gettid));
printf("\n");
balance -= 1000;
operation = 0;
}
//Se a operação for de saída ou reiniciar, a thread é finalizada.
else if (operation == EXIT_OPERATION || operation == RESTART_OPERATION) {
pthread_exit(0);
}
} while (1);
}
// Função de impressão.
void *print_function(void *arg) {
// Loop infinito para a thread ficar sempre ativa, esperando a operação ser chamada.
do {
if (operation == PRINT_OPERATION) {
printf("\n");
printf("==============================================\n");
printf("Print process TID: %ld\n", syscall(__NR_gettid));
printf("Balance: %d units\n", balance);
printf("\n");
operation = 0;
}
//Se a operação for de saída ou reiniciar, a thread é finalizada.
else if (operation == EXIT_OPERATION || operation == RESTART_OPERATION) {
pthread_exit(0);
}
} while (1);
}
// Função de reiniciar o sistema.
void *restart_function(void *arg) {
//Zera os valores de saldo e operação, limpa a tela e finaliza a thread.
balance = 0;
operation = 0;
system("clear");
pthread_exit(0);
}