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:

  1. El Problema del Productor-Consumidor
  2. El Problema dels Filòsofs Sopant
  3. El Problema dels Lectors i Escriptors

  1. 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 i full: 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 i consumer: Implementen la lògica per afegir i extreure elements del buffer.

  1. 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.

  1. 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 i mutex: rw_mutex controla l'accés exclusiu dels escriptors, mentre que mutex protegeix la variable read_count.
  • Funcions reader i writer: 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.

© Copyright 2024. Tots els drets reservats