En aquest tema, explorarem alguns dels problemes clàssics que es presenten en la concurrència dins dels sistemes operatius. Aquests problemes són fonamentals per comprendre els desafiaments i les solucions associades amb la gestió de múltiples processos i fils que s'executen simultàniament. Els problemes clàssics que tractarem són:
- El Problema del Productor-Consumidor
- El Problema dels Filòsofs Sopant
- El Problema dels Lectors i Escriptors
- El Problema del Productor-Consumidor
Descripció
El problema del productor-consumidor, també conegut com a problema del buffer limitat, involucra dos tipus de processos: productors i consumidors. Els productors generen dades i les col·loquen en un buffer, mentre que els consumidors extreuen dades del buffer per processar-les. El repte és assegurar que els productors no afegeixin dades a un buffer ple i que els consumidors no intentin extreure dades d'un buffer buit.
Solució amb Semàfors
Utilitzarem semàfors per sincronitzar l'accés al buffer compartit.
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#define BUFFER_SIZE 10
int buffer[BUFFER_SIZE];
int count = 0;
sem_t empty;
sem_t full;
pthread_mutex_t mutex;
void* producer(void* arg) {
int item;
while (1) {
item = rand(); // Produir un element
sem_wait(&empty);
pthread_mutex_lock(&mutex);
buffer[count++] = item;
printf("Produced: %d\n", item);
pthread_mutex_unlock(&mutex);
sem_post(&full);
}
}
void* consumer(void* arg) {
int item;
while (1) {
sem_wait(&full);
pthread_mutex_lock(&mutex);
item = buffer[--count];
printf("Consumed: %d\n", item);
pthread_mutex_unlock(&mutex);
sem_post(&empty);
}
}
int main() {
pthread_t prod, cons;
sem_init(&empty, 0, BUFFER_SIZE);
sem_init(&full, 0, 0);
pthread_mutex_init(&mutex, NULL);
pthread_create(&prod, NULL, producer, NULL);
pthread_create(&cons, NULL, consumer, NULL);
pthread_join(prod, NULL);
pthread_join(cons, NULL);
sem_destroy(&empty);
sem_destroy(&full);
pthread_mutex_destroy(&mutex);
return 0;
}Explicació del Codi
- Semàfors
emptyifull: Controlen el nombre d'espais buits i plens en el buffer, respectivament. - Mutex
mutex: Assegura que només un fil accedeixi al buffer a la vegada. - Funcions
producericonsumer: Implementen la lògica per afegir i extreure elements del buffer.
- El Problema dels Filòsofs Sopant
Descripció
Aquest problema modela cinc filòsofs asseguts al voltant d'una taula circular, on cadascun té un plat de menjar i un palet a cada costat. Els filòsofs poden pensar o menjar, però per menjar necessiten tenir els dos palets. El repte és evitar la fam i la interbloqueig.
Solució amb Semàfors
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#define N 5
sem_t chopsticks[N];
void* philosopher(void* num) {
int id = *(int*)num;
while (1) {
printf("Philosopher %d is thinking.\n", id);
sem_wait(&chopsticks[id]);
sem_wait(&chopsticks[(id + 1) % N]);
printf("Philosopher %d is eating.\n", id);
sem_post(&chopsticks[id]);
sem_post(&chopsticks[(id + 1) % N]);
}
}
int main() {
pthread_t phil[N];
int ids[N];
for (int i = 0; i < N; i++) {
sem_init(&chopsticks[i], 0, 1);
ids[i] = i;
}
for (int i = 0; i < N; i++) {
pthread_create(&phil[i], NULL, philosopher, &ids[i]);
}
for (int i = 0; i < N; i++) {
pthread_join(phil[i], NULL);
}
for (int i = 0; i < N; i++) {
sem_destroy(&chopsticks[i]);
}
return 0;
}Explicació del Codi
- Semàfors
chopsticks: Representen els palets, inicialitzats a 1 per indicar que estan disponibles. - Funció
philosopher: Cada filòsof agafa els dos palets necessaris per menjar i després els allibera.
- El Problema dels Lectors i Escriptors
Descripció
Aquest problema involucra múltiples processos lectors i escriptors que accedeixen a una base de dades compartida. Els lectors poden accedir simultàniament, però els escriptors necessiten accés exclusiu. El repte és evitar la fam dels escriptors i assegurar la consistència de les dades.
Solució amb Semàfors
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
sem_t rw_mutex;
sem_t mutex;
int read_count = 0;
void* reader(void* arg) {
while (1) {
sem_wait(&mutex);
read_count++;
if (read_count == 1) {
sem_wait(&rw_mutex);
}
sem_post(&mutex);
printf("Reader is reading.\n");
sem_wait(&mutex);
read_count--;
if (read_count == 0) {
sem_post(&rw_mutex);
}
sem_post(&mutex);
}
}
void* writer(void* arg) {
while (1) {
sem_wait(&rw_mutex);
printf("Writer is writing.\n");
sem_post(&rw_mutex);
}
}
int main() {
pthread_t r1, r2, w1;
sem_init(&rw_mutex, 0, 1);
sem_init(&mutex, 0, 1);
pthread_create(&r1, NULL, reader, NULL);
pthread_create(&r2, NULL, reader, NULL);
pthread_create(&w1, NULL, writer, NULL);
pthread_join(r1, NULL);
pthread_join(r2, NULL);
pthread_join(w1, NULL);
sem_destroy(&rw_mutex);
sem_destroy(&mutex);
return 0;
}Explicació del Codi
- Semàfors
rw_muteximutex:rw_mutexcontrola l'accés exclusiu dels escriptors, mentre quemutexprotegeix la variableread_count. - Funcions
readeriwriter: Implementen la lògica per permetre l'accés concurrent dels lectors i l'accés exclusiu dels escriptors.
Conclusió
Els problemes clàssics de concurrència són fonamentals per entendre els desafiaments de la programació concurrent i les tècniques de sincronització. Hem vist com utilitzar semàfors per resoldre aquests problemes i assegurar la correcta coordinació entre processos i fils. Aquests conceptes són essencials per desenvolupar sistemes operatius robustos i eficients.
Fonaments de Sistemes Operatius
Mòdul 1: Introducció als Sistemes Operatius
- Conceptes Bàsics de Sistemes Operatius
- Història i Evolució dels Sistemes Operatius
- Tipus de Sistemes Operatius
- Funcions Principals d'un Sistema Operatiu
Mòdul 2: Gestió de Recursos
Mòdul 3: Concurrència
- Conceptes de Concurrència
- Fils i Processos
- Sincronització i Exclusió Mútua
- Problemes Clàssics de Concurrència
