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
empty
ifull
: 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
producer
iconsumer
: 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_mutex
imutex
:rw_mutex
controla l'accés exclusiu dels escriptors, mentre quemutex
protegeix la variableread_count
. - Funcions
reader
iwriter
: 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