Leandro Lima

Lab1: Produtores e Consumidores

Introdução:

Esse problema ocorre em certos casos de acesso a recursos compartilhados. 2 processos dividem um buffer comum de tamanho fixo. Um deles, o produtor, coloca informação no buffer, enquanto o outro, o consumidor, a retira. O problema ocorre quando o produtor deseja colocar um novo item no buffer, mas ele ja está cheio. A saída é o produtor ir dormir e ser acordado quando o consumidor tiver removido um ou mais itens. De forma análoga, se o consumidor desejar remover um item do buffer e este estiver vazio, o consumidor é posto para dormir até que o produtor coloque um ou mais itens no buffer, acordando o consumidor.

Objetivos:

- Escrever um programa em C que implemente a solução para esse problema de sincronização utilizando Pipes e Processos.
- Testes com vários produtores e consumidores.
- Verificar o estado dos processos com o comando "ps".
- Criar processos Zumbis.

Código-fonte do programa:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
 
#define N        100
#define READ    0
#define WRITE    1
 
void producer(void);
void consumer(void);
int produce_item(void);
void insert_item(int item);
int remove_item(void);
void consume_item(int item);
 
int fd[2], bytesRead;
int message;
 
int main()
{
    int i,a;
    pipe(fd);
    srand(time(NULL));
 
    for(i=0; i<5;i++)
    {
        if(fork() == 0)
        {
            if(i%2 == 0)
            {        
                close(fd[READ]);
                producer();
                exit(0);
            } else
            {
                close(fd[WRITE]);
                consumer();
                exit(0);
            }
        }
    }
 
    if(fork() != 0) while(1);
    exit(0);
 
}
 
void producer(void)
{
    int item, i;
 
    while(1)
    {
        item = produce_item();
        insert_item(item);
        sleep(1);
    }
}
 
void consumer(void)
{
    int item, i;
 
    while(1)
    {
        item = remove_item();    
        consume_item(item);
        sleep(1);
    }
}
 
int produce_item()
{
    int aux = getpid();
 
    printf("Item produced: My ID is %d\n", aux);
 
    return aux;
 
}
 
void insert_item(int item)
{
    write (fd[WRITE], &item, sizeof(int));
}
 
int remove_item()
{
    bytesRead = read(fd[READ], &message, sizeof(int));
    printf ("Read %d bytes. Message was: %d\n", bytesRead, message);
 
    return message;
}
 
void consume_item(int item)
{
    printf("Item consumed at %d: %d\n\n", getpid(), item);
}

Comentários:

Os processos filhos são os encarregados de gerar os produtores e os consumidores. O processo zumbi é criado no final do código da função main e verificado com o comando "ps ax". Vale lembrar que os pipes criados são thread-safe, portanto não há necessidade de se implementar os métodos referentes ao acesso exclusivo a determinadas variáveis.

Teste (saída do terminal):

Item produced: My ID is 10713
Read 4 bytes. Message was: 10713
Item consumed at 10714: 10713

Item produced: My ID is 10715
Read 4 bytes. Message was: 10715
Item consumed at 10716: 10715

Item produced: My ID is 10717
Item produced: My ID is 10713
Read 4 bytes. Message was: 10717
Item consumed at 10714: 10717

Item produced: My ID is 10715
Read 4 bytes. Message was: 10713
Item consumed at 10716: 10713

Item produced: My ID is 10717
Item produced: My ID is 10713
Read 4 bytes. Message was: 10715
Item consumed at 10714: 10715

Item produced: My ID is 10715
Read 4 bytes. Message was: 10717
Item consumed at 10716: 10717

Item produced: My ID is 10717
Read 4 bytes. Message was: 10713
Item consumed at 10714: 10713

Item produced: My ID is 10713
Item produced: My ID is 10715
Read 4 bytes. Message was: 10715
Item consumed at 10716: 10715

Item produced: My ID is 10717
Item produced: My ID is 10713
Read 4 bytes. Message was: 10717
Item consumed at 10714: 10717

Item produced: My ID is 10715
Read 4 bytes. Message was: 10713
Item consumed at 10716: 10713

Item produced: My ID is 10717
Read 4 bytes. Message was: 10715
Item consumed at 10714: 10715

Item produced: My ID is 10713
Item produced: My ID is 10715
Read 4 bytes. Message was: 10717
Item consumed at 10716: 10717

Item produced: My ID is 10717
Item produced: My ID is 10713
Read 4 bytes. Message was: 10713
Item consumed at 10714: 10713

Item produced: My ID is 10715
Read 4 bytes. Message was: 10715
Item consumed at 10716: 10715

Lab2: Jantar dos Filósofos

Introdução:

O Jantar dos Filósofos é problema clássico de sincronização proposto por Dijkstra em 1965. Essa situação pode ser descrita da seguinte forma:
5 filósofos sentam-se ao redor de uma mesa circular e cada filósofo possui um prato de espaguete, que será degustado com 2 garfos. Contudo, há apenas 5 garfos na mesa, dispostos, cada um, à esquerda e à direita de um prato para serem compartilhados entre os filósofos. Isto gera um impasse, pois se todos forem comer ao mesmo tempo, não haverá garfos o suficiente.
A vida de um filósofo consiste em períodos alternados das ações "comer" e "pensar". Quando um deles fica com fome, ele tenta pegar o garfo da esquerda e o da direita, um de cada vez. Se conseguir pegar os 2 garfos, o filósofo come por um tempo, coloca os garfos de volta à mesa e volta a pensar.

Objetivo:

Escrever um programa em C que implemente a solução para esse problema de sincronização utilizando Threads.

Código-fonte do programa:

#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <semaphore.h>
 
#define N            5
#define LEFT        (i+N-1)%N
#define RIGHT        (i+1)%N
#define THINKING        0
#define HUNGRY        1
#define EATING        2
#define TRUE        1
 
void* philosopher(void*);
void take_forks(int);
void put_forks(int);
void think(void);
void eat(void);
void test(int);
 
int state[N];
sem_t mutex;
sem_t s[N];
 
int main()
{
    int res, t;
    pthread_t filosofo[5];
    int filo[]={1,2,3,4,5};
    sem_init(&mutex, 0, 1);
 
    for(t=0; t<N; t++) sem_init(&s[t], 0, 0);
 
    for(t=0; t<=4; t++)
    {
        res = pthread_create(&filosofo[t], NULL, philosopher, (void*) (filo+t));
        if(res != 0)
        {
            perror("Falha na criacao da Thread");
            exit(EXIT_FAILURE);
        }
    }
 
    res = pthread_join(filosofo[0], NULL);
    if(res != 0)
    {
        perror("Falha na juncao da Thread\n");
        exit(EXIT_FAILURE);
    }
 
    return 0;
}
 
void *philosopher(void * ag)
{
 
    int i = *(int*)ag;
    while(TRUE)
    {
        think();
        take_forks(i);
        eat();
        put_forks(i);
    }
 
}
 
void take_forks(int i)
{
    sem_wait(&mutex);
    printf("%d vai pegar os garfos.\n", i);
    state[i] = HUNGRY;
    test(i);
    sem_post(&mutex);
    sem_wait(&s[i]);
}
 
void put_forks(int i)
{
    sem_wait(&mutex);
    printf("%d coloca os garfos de volta e vai pensar.\n", i);
    state[i] = THINKING;
    test(LEFT);
    test(RIGHT);
    sem_post(&mutex);
}
 
void test(int i)
{
    if(state[i] == HUNGRY && state[LEFT] != EATING && state[RIGHT] != EATING)
    {
        printf("%d vai comer.\n", i);
        state[i] = EATING;
        sem_post(&s[i]);
    }
}
 
void think()
{
    printf("\nPensando.\n");
    sleep(1);    
}
 
void eat()
{
    printf("\nComendo.\n");
    sleep(1);
}

Comentários:

A solução para esse problema é muito útil em processos de modelagem que competem por acesso exclusivo a um número limitado de recursos como, por exemplo, dispositivos de I/O. Não foi implementada a verificação da ordem de pegada dos garfos, pois não há corrida da forma como foi feito esse trecho de código. A verificação foi feita levando em consideração que, ao mesmo tempo, somente 2 filósofos não adjacentes podem ocupar a mesa.

Teste (saída do terminal):

Pensando.

Pensando.

Pensando.

Pensando.

Pensando.
2 vai pegar os garfos.
2 vai comer.

Comendo.
4 vai pegar os garfos.
4 vai comer.

Comendo.
3 vai pegar os garfos.
1 vai pegar os garfos.
5 vai pegar os garfos.
2 coloca os garfos de volta e vai pensar.
1 vai comer.

Comendo.
4 coloca os garfos de volta e vai pensar.

Lab3: Barbeiro Dorminhoco

Introdução:

Outro problema de comunicação interprocessos se passa em uma barbearia. A barbearia possui um barbeiro, uma cadeira para o corte e N cadeiras para clientes que esperam. Se não houver clientes presentes, o barbeiro se senta na cadeira de corte e dorme. Quando um cliente chega, ele tem de acordar o barbeiro. Se mais clientes chegarem enquanto o barbeiro está cortando o cabelo de um cliente, eles podem se sentar (se houver cadeiras) ou deixar a barbearia (se todas as cadeiras estiverem ocupadas).

Objetivo:

Escrever um programa em C que implemente a solução para esse problema de sincronização utilizando Threads.

Código-fonte do programa:

#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <semaphore.h>
 
#define CHAIRS    5
#define TRUE    1
 
void* barber(void*);
void* customer(void*);
void cut_hair(void);
void get_haircut(int);
 
sem_t customers;
sem_t barbers;
sem_t mutex;
int waiting = 0;
 
int main()
{
    int res, t;
    pthread_t* cliente;
    pthread_t barbeiro;
    sem_init(&mutex, 0, 1);
    sem_init(&barbers, 0, 0);
    sem_init(&customers, 0, 0);
 
    res = pthread_create(&barbeiro, NULL, barber, NULL);
    if(res != 0)
    {
        perror("Falha na criacao da Thread do Barbeiro\n");
        exit(EXIT_FAILURE);
    }
 
    for(t=0; 1; t++)
    {
 
        cliente = (pthread_t*) malloc(sizeof(pthread_t));
        res = pthread_create(cliente, NULL, customer,(void *)&t);
        if(res != 0)
        {
            perror("Falha na criacao da Thread do Cliente");
            exit(EXIT_FAILURE);
        }
 
        sleep(1);    
    }
 
    return 0;
}
 
void* barber(void* ag)
{
    while(TRUE)
    {
        sem_wait(&customers);
        sem_wait(&mutex);
        waiting = waiting - 1;
        sem_post(&barbers);
        sem_post(&mutex);
        cut_hair();
    }
}
 
void* customer(void* ag)
{
    int k = *(int *)ag;
    sem_wait(&mutex);
    if(waiting < CHAIRS)
    {
        waiting = waiting + 1;
        sem_post(&customers);
        sem_post(&mutex);
        sem_wait(&barbers);
        get_haircut(k);
    } else
    {
        printf("Fila cheia. Vou embora.\n");
        sem_post(&mutex);
    }
}
 
void cut_hair()
{
    printf("Barbeiro cortando cabelo.\n");
    sleep(1);
}
 
void get_haircut(int k)
{
    printf("Cliente %d: fui atendido.\n",k);
    sleep(1);
}

Comentários:

Esse problema é similar a diversas situações de enfileiramento, como em serviços de auxílio a redes com chamada computadorizada aguardando o sistema para colocar em espera um número limitado de chamadas recebidas. A ilimitação do número de clientes entrando na loja é um fato que ilustra bem a questão, mas, em termos de testes, percebemos a falta de espaços em cadeiras a partir de um certo número de clientes que depende de quanto tempo cada um fica no corte de cabelo.

Teste (saída do terminal):

Cliente 0: fui atendido.
Barbeiro cortando cabelo.
Cliente 1: fui atendido.
Barbeiro cortando cabelo.
Cliente 2: fui atendido.
Barbeiro cortando cabelo.
Cliente 3: fui atendido.
Barbeiro cortando cabelo.
Cliente 4: fui atendido.
Barbeiro cortando cabelo.
Cliente 5: fui atendido.
Barbeiro cortando cabelo.
Cliente 6: fui atendido.
Barbeiro cortando cabelo.
Cliente 7: fui atendido.
Barbeiro cortando cabelo.
Cliente 8: fui atendido.
Barbeiro cortando cabelo.
Cliente 9: fui atendido.
Barbeiro cortando cabelo.
Cliente 10: fui atendido.
Barbeiro cortando cabelo.

Lab4: Leitores e Escritores

Introdução:

Esse problema é uma típica modelagem de acesso a um banco de dados. Numa situação hipotética, consideremos um sistema de reservas de hotéis, com muitos processos competindo para ler e escrever no sistema. É aceitável ter vários processos lendo o banco de dados ao mesmo tempo, porém se um processo estiver atualizando, ou seja, escrevendo no banco de dados, nenhum outro processo pode ter acesso a esse banco, nem mesmo os leitores.

Objetivo:

Escrever um programa em C que implemente a solução para esse problema de sincronização utilizando Mutex e Semáforos.

Código-fonte do programa:

#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <semaphore.h>
 
#define L        5 // numero de leitores
#define E        3 // numero de escritores
#define TRUE    1
 
void* reader(void*);
void* writer(void*);
void read_data_base(int);
void use_data_read(int);
void think_up_data(void);
void write_data_base(void);
 
sem_t mutex;
sem_t db;
int rc = 0;
 
int main()
{
    int res, t;
    pthread_t leitor[L];
    pthread_t escritor[E];
    int leit[L];
    sem_init(&mutex, 0, 1);
    sem_init(&db, 0, 1);
 
    for(t=0; t<L; t++)
    {
        leit[t] = t;        
        res = pthread_create(&leitor[t], NULL, reader,(void*)&t);
        if(res != 0)
        {
            perror("Falha na criacao da Thread");
            exit(EXIT_FAILURE);
        }
    }
 
    for(t=0; t<E; t++)
    {
        res = pthread_create(&escritor[t], NULL, writer, NULL);
        if(res != 0)
        {
            perror("Falha na criacao da Thread\n");
            exit(EXIT_FAILURE);
        }
    }
 
    res = pthread_join(leitor[0], NULL);
    if(res != 0)
    {
        perror("Falha na juncao da Thread.\n");
        exit(EXIT_FAILURE);
    }
 
    return 0;
}
 
void* reader(void* ag)
{
int k = *(int *) ag;
    while(TRUE)
    {
        sem_wait(&mutex);
        rc = rc + 1;
        if(rc == 1) sem_wait(&db);
        sem_post(&mutex);
        read_data_base(k);
        sem_wait(&mutex);
        rc = rc - 1;
        if(rc == 0) sem_post(&db);
        sem_post(&mutex);
        use_data_read(k);
    }
}
 
void* writer(void* ag)
{
    while(TRUE)
    {
        think_up_data();
        sem_wait(&db);
        write_data_base();
        sem_post(&db);        
    }
}
 
void read_data_base(int k)
{
    printf("Leitor %d: Estou lendo.\n",k);
    sleep(1);
}
 
void use_data_read(int k)
{
    printf("Leitor %d: Saí da reg. crítica.\n",k);
    sleep(1);
}
 
void think_up_data()
{
    printf("Irei entrar na reg. crítica.\n");
    sleep(1);
}
 
void write_data_base()
{
    printf("Estou escrevendo.\n");
    sleep(1);
}

Comentários:

Esse problema modela basicamente um acesso a um banco de dados. Assim, a idéia principal é a de que vários leitores consigam dividir o acesso, porém escritores, em circunstância alguma, devem ter ou prover acesso livre aos dados. A variável "db" controla esse acesso, enquanto a "mutex" controla o acesso a variável de contagem de leitores acessando o banco.

Teste (saída do terminal):

Leitor 1: Estou lendo.
Leitor 1: Estou lendo.
Leitor 3: Estou lendo.
Leitor 4: Estou lendo.
Leitor 0: Estou lendo.
Irei entrar na reg. crítica.
Irei entrar na reg. crítica.
Irei entrar na reg. crítica.
Leitor 1: Saí da reg. crítica.
Leitor 0: Saí da reg. crítica.
Leitor 4: Saí da reg. crítica.
Leitor 3: Saí da reg. crítica.
Leitor 1: Saí da reg. crítica.
Estou escrevendo.
Irei entrar na reg. crítica.
Estou escrevendo.
Irei entrar na reg. crítica.
Estou escrevendo.
Irei entrar na reg. crítica.
Leitor 1: Estou lendo.
Leitor 3: Estou lendo.
Leitor 1: Estou lendo.
Leitor 4: Estou lendo.
Leitor 0: Estou lendo.
Leitor 3: Saí da reg. crítica.
Leitor 4: Saí da reg. crítica.
Leitor 0: Saí da reg. crítica.
Leitor 1: Saí da reg. crítica.
Leitor 1: Saí da reg. crítica.
Estou escrevendo.
Leitor 1: Estou lendo.
Irei entrar na reg. crítica.
Leitor 0: Estou lendo.
Leitor 3: Estou lendo.
Leitor 4: Estou lendo.
Leitor 1: Estou lendo.
Leitor 4: Saí da reg. crítica.
Leitor 3: Saí da reg. crítica.
Leitor 1: Saí da reg. crítica.
Leitor 0: Saí da reg. crítica.
Leitor 1: Saí da reg. crítica.
Estou escrevendo.
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License