Relatório - CES33

INSTITUTO TECNOLÓGICO DE AERONÁUTICA
Aluno: Luty Rodrigues Ribeiro Professor: Yano
Curso: 2º ano de Engenharia de Computação

Este relatório tem por objetivo mostrar a aplicação dos conceitos de processos, threads, semáforos etc. nos quatro problemas clássicos de sistemas operacionais apresentados abaixo.
Mostramos a seguir os códigos que implementam a solução para os quatro problemas.
É bom salientar que, para compilar os códigos abaixo, é preciso fazer:
gcc codigo.c -o codigo -lpthread

Primeiro Problema: Produtor x Consumidor

Neste problema, a condição imposta foi de resolvê-lo com o uso de processos e pipes, e poder utilizar vários produtores e consumidores.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
 
#define READ  0
#define WRITE 1
#define TRUE  1
#define FALSE 0
 
int fd[2];
 
void criarProdutor();
void criarConsumidor();
void produtor(void);
void consumidor(void);
 
int main() {
    int i, p, c;
    pipe(fd);
 
    printf("De o numero de produtores: ");
    scanf("%d",&p);
    printf("De o número de consumidores: ");
    scanf("%d",&c);
 
    for(i=0; i<p; i++) criarProdutor();
    for(i=0; i<c; i++) criarConsumidor();
 
    while(TRUE);
}
 
void criarProdutor() {
    if(fork() == 0) {
        produtor();
        exit(0);
    }
}
 
void criarConsumidor() {
    if(fork() == 0) {
        consumidor();
        exit(0);
    }
}
 
void produtor(void){
    int item;    
 
    close(fd[READ]);
    while(TRUE){    
        //produzimos item em intervalo aleatório
        sleep(rand()%4);
        //Produção do item
        item = getpid();
        printf("Produtor fabricou o item %d\n",item);
        write(fd[WRITE], &item, sizeof(int));    
    }
    close(fd[WRITE]);
}
 
void consumidor(void) {
    int item;
 
    close(fd[WRITE]);
    while(TRUE){
        //produzimos item em intervalo aleatório
        sleep(rand()%4);
        if(read(fd[READ], &item, sizeof(int)) != -1) {
            printf("Processo %d consumiu o item %d\n", getpid() ,item);
        }
    }
    close(fd[READ]);
}

Compilando e executando o código acima, obtemos a tela de saída a seguir (depois de dar ctrl+c):

Tarefa1.png

Temos acima os produtores e consumidores identificados pelo PID (process ID).
Se deixarmos o programa executando e observamos os processos (com o comando ps ax), podemos verificar o estado dos processos envolvidos.
Por exemplo, numa execução em que escolhemos ter 3 produtores e 3 consumidores, a saída obtida será:

Tarefa1ps.png

Na saída acima, vemos que há 7 processos ./tarefa1, onde 6 deles estão suspensos ('S') e um deles (o processo main) está executando ('R').
Durante a tentativa de implementar o programa acima, em certo ponto, obtive o programa:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
 
#define READ 0 /* The index of the read end of the pipe */ 
#define WRITE 1 /* The index of the write end of the pipe*/
#define TRUE 1
#define N 1
 
//vetor que guardará os descritores do pipe
int fd[2];
 
void insert_item(char* item);
void producer(void);
void consumer(void) ;
void consume_item(char* item);
 
int main () {       
    int p, c;
    int i;
    pid_t pid_do_pai = getpid();
    pid_t pid_filho1;    
    pipe (fd); /*Create an unnamed pipe */ 
 
    printf("De o numero de produtores: ");
    scanf("%d",&p);
    printf("De o número de consumidores: ");
    scanf("%d",&c);
 
    if(fork() == 0) pid_filho1 = getpid();
 
    //gerando p produtores
    //só o processo pai gerará produtores
    if(pid_do_pai == getpid()) {
         for(i=0; i<p; i++) {
            if(fork() == 0) {
                producer();
                exit(0);
            }
        }
    }
 
    //gerando c consumidores
    //só o processo filho1 gerará consumidores
    if(pid_filho1 == getpid() ) {
        for(i=0; i<c; i++) {
            if(fork() == 0) {
                consumer();
                exit(0);
            }
        }
    }
 
    while(TRUE);
 
}
 
void insert_item(char* item) {
    close(fd[READ]);
    write(fd[WRITE], item, strlen(item)+1);
    close(fd[WRITE]);
    sleep(1);
}
 
void producer(void){
    char *item;    
 
    printf("Produtor %d\n", getpid());    
    while(TRUE){
        //Produção do item
        sprintf(item, "produzido por %d",getpid() );        
        printf("Produtor %d\n", getpid());    
        insert_item(item);
    }
}
 
void consumer(void) {
    char *item;
    printf("Consumidor %d\n",getpid()); 
    while(TRUE){
        close(fd[WRITE]);
        read(fd[READ], item, 80);
        close(fd[READ]);
        if(item != NULL)
            consume_item(item);
    }
}
 
void consume_item(char* item) {
    sleep(1);
    printf("Foi consumido o item %s\n", item);
}

Executemos esse código escolhendo, por exemplo, 1 produtor e 1 consumidor.
Apesar de parecer coerente à primeira vista, o programa trava assim que entra nas funções producer e consumer, como mostrado na tela abaixo:
tarefa1zumbi.png

Percebemos que aparece uma mensagem com PID do producer e deveria aparecer uma mensagem idêntica em seguida, mas esta não aparece.
Se formos observar o estado dos processos, temos o seguinte:

tarefa1estado.png

Temos 4 processos relativos à ./tarefa1, e um deles apresenta a sigla Z+. Este 'Z' indica justamente que este processo está em estado zumbi.
Isto ocorre porque, provavelmente, houve alguma falha na função sprintf(), travando assim o processo produtor (note pelo ID, que é o processo produtor que entra em estado zumbi).

Segundo Problema: Jantar dos Filósofos

Para este problema, a condição era utilizar threads.
A idéia é usar cada thread como um filósofo, e as ações dos filósofos estão codificadas nas funções auxiliares.

#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <pthread.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
#define FALSE        0
 
int state[N];
sem_t mutex;
sem_t s[N];
 
void up(sem_t *sem) ;
void down(sem_t *sem);
void eat(int i) ;
void *philosopher(void *arg) ;
void take_forks(int i) ;
void put_forks(int i) ;
void test(int i);
 
int main(){
    int i;
 
    //Inicialização dos semáforos
    sem_init(&mutex, TRUE, 1);
    for(i=0; i<N; i++) sem_init(&s[i], TRUE, 1);
 
    pthread_t filosofo[5];
 
    for(i=0; i<5; i++)
        pthread_create(&filosofo[i], NULL, philosopher, (void *)&i);
 
    for(i=0; i<5; i++)
        pthread_join(filosofo[i], NULL );    
 
    exit(EXIT_SUCCESS);
}
 
void up(sem_t *sem) {
    sem_post(sem);
}
 
void down(sem_t *sem) {
    sem_wait(sem);
}
 
void eat(int i) {
    printf("Filósofo %d: \"hummmm...spaghetti!! \"\n", i);
    sleep(1);
}
 
void think(int i) {
 
    printf("Filósofo %d, \"Agora estou pensando...\"\n", i);
    sleep(2);
}
 
void *philosopher(void *arg) {
    int *pt = arg;
    int valor = *pt;
    while(TRUE) {
        think(valor);
        take_forks(valor);
        eat(valor);
        put_forks(valor);    
    }
 
    pthread_exit(NULL);
}
 
void take_forks(int i) {
    down(&mutex);
    state[i] = HUNGRY;
    test(i);
    up(&mutex);
    down(&s[i]);
}
 
void put_forks(int i) {
    down(&mutex);
    state[i] = THINKING;
    test((i+N-1)%N); //testa lado esquerdo
    test((i+1)%N);    //teste lado direito
    up(&mutex);
}
 
void test(int i){
    if (state[i] == HUNGRY && state[(i+N-1)%N] != EATING && state[(i+1)%N] != EATING) {
        state[i] = EATING;
        up(&s[i]);    
    }
}

Complilando o código acima e executando-o (./codigo), obtemos como saída:
luty@luty-laptop:~/Desktop/Programando/Tarefa2$ ./tarefa2
Filósofo 0, "Agora estou pensando…"
Filósofo 1, "Agora estou pensando…"
Filósofo 2, "Agora estou pensando…"
Filósofo 3, "Agora estou pensando…"
Filósofo 4, "Agora estou pensando…"
Filósofo 1: "hummmm…spaghetti!! "
Filósofo 2: "hummmm…spaghetti!! "
Filósofo 3: "hummmm…spaghetti!! "
Filósofo 4: "hummmm…spaghetti!! "
Filósofo 0: "hummmm…spaghetti!! "
Filósofo 2, "Agora estou pensando…"
Filósofo 4, "Agora estou pensando…"
Filósofo 3, "Agora estou pensando…"
Filósofo 0, "Agora estou pensando…"
Filósofo 1, "Agora estou pensando…"
Filósofo 3: "hummmm…spaghetti!! "
Filósofo 0: "hummmm…spaghetti!! "
Filósofo 1: "hummmm…spaghetti!! "
Filósofo 0, "Agora estou pensando…"
Filósofo 1, "Agora estou pensando…"
Filósofo 4: "hummmm…spaghetti!! "
Filósofo 2: "hummmm…spaghetti!! "
Filósofo 3, "Agora estou pensando…"
Filósofo 2, "Agora estou pensando…"
Filósofo 4, "Agora estou pensando…"
^C
luty@luty-laptop:~/Desktop/Programando/Tarefa2$

Se não damos ctrl+c, o programa executará eternamente.
A saída acima nos dá uma idéia da interação entre os filosófos.

Terceiro Problema: Barbeiro

Neste programa, também deveríamos implementá-lo com o uso de threads.
Esta implentação utiliza uma thread como o barbeiro, e são criadas, indefinidamente, outras threads para serem os clientes.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
#include <pthread.h>
 
#define CHAIRS 5
#define TRUE    1
#define FALSE     0
 
sem_t customers; //inicia em 0
sem_t barbers;  //inicia em 0
sem_t mutex;  //inicia em 1
int waiting = 0;
 
void down(sem_t *sem);
void up(sem_t *sem);
void cut_hair();
void *barber(void *arg);
void get_haircut();
void *customer(void *arg);
 
int cont = 0;
 
int main(){
    sem_init(&customers, TRUE, 0);
    sem_init(&barbers, TRUE, 0);
    sem_init(&mutex, TRUE, 1);
 
    pthread_t barbeiro;
    pthread_t cliente;
 
    pthread_create(&barbeiro, NULL, barber, NULL);
    while(TRUE) {
        pthread_create(&cliente, NULL, customer, NULL);
        sleep(1);
    }
 
    return 0;
}
 
void down(sem_t *sem){
    sem_wait(sem);
}
 
void up(sem_t *sem){
    sem_post(sem);
}
 
void cut_hair(){
    sleep(4);
}
 
void *barber(void *arg){
    printf("Barbeiro: \"Mais um dia de trabalho...\"\n");
    while(TRUE){
        down(&customers);
        down(&mutex);
        waiting = waiting - 1;
        up(&barbers);
        up(&mutex);
        cut_hair();
    }
}
 
void get_haircut(int num) {
    printf("Cliente %d: \"Finalmente! Consegui cortar meu cabelo!!\"\n", num);
}
 
void *customer(void *arg){
    int cliente = cont++;
 
    down(&mutex);
    if(waiting<CHAIRS) {
        printf("Cliente %d: \"Vim cortar meu cabelo...\"\n", cliente);
        waiting = waiting + 1;
        up(&customers);
        up(&mutex);
        down(&barbers);
        get_haircut(cliente);
    }
    else {
        up(&mutex);
        printf("Cliente %d: \"Está muito cheio hoje...\"\n", cliente);
    }
    pthread_exit(NULL);
}

Compilando o código acima e executando-o, temos a saída a seguir:

Tarefa3.png

Novamente, precisamos encerrar a execução com ctrl+c. Podemos perceber o funcionamento do programa quando, em alguns momentos, vemos alguns clientes nem entrarem, justamente quando há cinco pessoas já esperando para cortar o cabelo.

Quarto Problema: Leitores e Escritores

Este problema exigia o uso de mutex e semáforos.
A implementação abaixo utilizará threads, onde cada thread será um leitor ou um escritor.
Particularizamos este código para gerar 2 escritores e 4 leitores.

#include <stdio.h>
#include <semaphore.h>
#include <pthread.h>
 
#define TRUE    1
#define FALSE    0
 
sem_t mutex;
sem_t db;
int rc = 0;
 
void *reader(void *arg);
void *writer(void *arg);
void down(sem_t *sem);
void up(sem_t *sem);
void read_data_base(int num);
void use_data_read(int num);
void think_up_data(int num);
void write_data_base(int num);
 
int main(){
    sem_init(&mutex, 0, 1);
    sem_init(&db, 0, 1);
 
    int i, j;
    int index_reader[5];
    int index_writer[5];
 
    for(i=0; i<5; i++) index_reader[i] = i;
    for(j=0; j<5; j++) index_writer[j] = j;
 
    pthread_t leitor[5];
    pthread_t escritor[5];
 
    for(j=0; j<2; j++)
        pthread_create(&escritor[j], NULL, writer, (void *)&index_writer[j]);
 
    for(i=0; i<4;i++)
        pthread_create(&leitor[i], NULL, reader, (void *)&index_reader[i]);
 
    while(TRUE);    
}
 
void down(sem_t *sem){
    sem_wait(sem);
}
 
void up(sem_t *sem){
    sem_post(sem);
}
 
void read_data_base(int num) {
    printf("Leitor %d: \"Estou lendo dado...\"\n", num);
    sleep(2);
}
 
void use_data_read(int num) {
    printf("Leitor %d: \"Dado lido e sendo usado...\"\n", num);
    sleep(2);
}
 
void *reader(void *arg) {
    int *pt = (int *)arg;
    int valor = *pt;    
 
    while(TRUE) {
        down(&mutex);
        rc++;
        if (rc == 1) down(&db);
        up(&mutex);
        read_data_base(valor);
        down(&mutex);
        rc--;
        if (rc == 0) up(&db);
        up(&mutex);
        use_data_read(valor);
    }        
}
 
void think_up_data(int num) {
    printf("Escritor %d: \"Processando dados...\"\n", num);
    sleep(1);
}
 
void write_data_base(int num) {
    printf("Escritor %d: \"Dados escritos na base de dados.\"\n", num);
} 
 
void *writer(void *arg) {
    int *pt = (int *)arg;
    int valor = *pt;
 
    while(TRUE) {
        think_up_data(valor);
        down(&db);
        write_data_base(valor);
        up(&db);    
    }
}

Compilando e executando o código acima, obtemos como saída:

prob4.png

Novamente, usamos ctrl+c para encerrar a execução do programa.

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License