Sistemas Operacionais, 10 semestre/2004

Experimento # 6

Código Fonte do Programa Exemplo

1. Introdução

O experimento anterior deve ter permitido ao aluno familiarizar-se com threads, além de permitir-lhe saber diferenciar o conceito de processo com aquele de thread. Também foi visto o problema de produtores e consumidores, baseado em um buffer circular.

Deseja-se neste experimento que o aluno combine threads com semáforos, desenvolvendo uma segunda solução para o problema dos produtores e consumidores.

Este exercício foi definido especialmente para as aulas de Sistemas Operacionais, na Faculdade de Engenharia de Computação, na PUC-Campinas, com a ajuda do bolsista alemão Florian Weizenegger.

2. Objetivos

A seguir estão os objetivos deste experimento com relação ao aluno:

Perceber que semáforos podem também controlar problemas entre threads.

Comparar soluções diferentes para o problema dos produtores e consumidores.

Conhecer outras chamadas ao sistema para manipulação de threads.

3. Teoria

O programa exemplo deste experimento praticamente é igual ao programa exemplo usado no experimento anterior. Procure perceber as diferenças, quanto ao término dos threads, através do comando pthread_join. Repare no uso do pthread_mutex para evitar a condição de corrida, e também dos comandos pthread_mutex_init, pthread_mutex_lock, pthread_mutex_unlock e pthread_mutex_destroy.

 

 

int myadd(int toAdd) {
  //buffer full
  if ((rp != (wp+1)) && (wp != rp + SIZEOFBUFFER - 1)) {
    *wp = toAdd;
    wp++;
    //buffer at its end
    if (wp == (start + SIZEOFBUFFER)) {
      wp = start;
    }
    return 1;
  } else return 0;
}

Embora os ponteiros rp e wp sejam recursos comuns, sejam alterados e acessados tanto por produtores como consumidores, determinando assim condições de corrida, repare que, por exemplo, rp, que é atualizado em myremove() em dois lugares, não afeta a decisão realizada em myadd().  Suponha que a decisão seja verdadeira e em seguida o valor de rp seja alterado, se era verdadeira, o buffer não estava cheio e com a alteração de rp este ficou mais vazio, portanto não acarreta problema maior. Se a decisão era falsa, o buffer estava cheio, a alteração de rp antes ou depois não acarreta em maiores conseqüências, pois apenas vai indicar que uma posição no buffer ficou livre e na próxima tentativa de myadd() haverá a inserção.

Então, pode-se diminuir a contenção causada pelo uso de um único mutex, através do uso de dois mutex, um para produtores e outro para consumidores.

 

int myremove() {
  //buffer empty
  if (wp != rp) {
    int retValue = *rp;
    rp++;
    if (rp == (start + SIZEOFBUFFER)) {
      rp = start;
    }
    return retValue;
  } else return 0;
}

 

Considere agora a seguinte solução para o problema dos produtores e consumidores com buffer Buf, de tamanho finito, circular para conter b mensagens, que são colocadas em um extremo e retiradas do outro com o auxílio de dois apontadores i1 e i2

 

            Esta solução utiliza quatro semáforos:

 

em-p: para exclusão mútua de produtores, valor inicial 1

 

em-c: para exclusão mútua de consumidores, valor inicial 1

 

o: número de posições do buffer ocupadas com mensagens, valor inicial 0, permite bloqueio de consumidores quando buffer está vazio

 

l: número de posições livres do buffer, valor inicial b, permite o bloqueio de produtores quando buffer está cheio

 

   i1 = b - 1

   i2 = 0

 

 

Produtor                                                      Consumidor

 

While true do                                                While true do

begin                                                             begin

     produz (msg_prod);                                    p (o);

     p (l);                                                              p (em-c);

     p (em-p);                                                      msg_cons:=Buf [i2];

     i1:=(i1+1) mod b;                                        i2:=(i2+1) mod b;

     Buf [i1]:= msg_prod;                                   v (l);

     v (o);                                                             v (em-c);

     v (em-p);                                                      consome (msg_cons);

end;                                                               end;

 

Repare três coisas nessa solução: o uso da operação módulo (%) para realizar o acesso circular ao buffer; o uso de dois semáforos para obtenção de exclusão mútua, um, em-p, para produtores e um, em-c, para consumidores; e o uso de semáforos para controle de buffer cheio, l, e buffer vazio, o.

4. Resultado

Cada experimento constitui uma atividade que precisa de ser completada através de duas tarefas básicas, que se referem ao entendimento e à modificação e compilação do programa exemplo que trata de assuntos relacionados com aqueles cobertos em sala de aula e na teoria.

Este experimento deve ser acompanhado de um relatório com as seguintes partes obrigatórias:

Introdução, indicando, em não mais do que 20 linhas, quais os objetivos do experimento;

Resultados das execuções do programa exemplo modificado e tarefa;  

Análise dos resultados;

Conclusão indicando o que foi aprendido com o experimento.

A entrega do trabalho deve ocorrer através do envio de um e-mail "Encaminhando programa 6", de acordo com o cronograma previamente estabelecido. A data e hora limites correspondem à segunda-feira às 24:00, da semana marcada para entrega e apresentação. Anexos a esse e-mail devem constar:

os programas fonte modificados para as tarefas,

os correspondentes executáveis,

o relatório final do trabalho e

uma imagem do comando ls -l sobre os arquivos usados no experimento ao final do mesmo.

A falta de qualquer elemento no e-mail ou a perda da data de entrega implica na perda da nota correspondente. Somente duas exceções serão consideradas, o fechamento do laboratório durante o período disponibilizado para a realização do experimento, ou problema de doença avisado com antecedência mínima de dois dias, antes da data da entrega.

Laboratório cheio, quedas de máquinas, falta de linha telefônica, problemas pessoais ou "blackouts" não serão aceitos como desculpas por atrasos. Por isso, recomenda-se fortemente que o início do trabalho ocorra o mais rapidamente possível.

Dica para compilação

A biblioteca pthread necessita ser adicionada quando da compilação dos programas que manipulam threads. Para isso use a diretiva:

gcc -o experiment-x2 -lpthread experiment-x2.c

 

Tarefas

As tarefas para este experimento são:

5. Apresentação

O resultado do experimento será apresentado em sala de aula no dia de aula prática da semana marcada para a entrega, com a presença obrigatória de todos os alunos, de acordo com o cronograma previamente estabelecido.

Serão escolhidos alunos para a apresentação e discussão do resultado. A critério do professor pode, inclusive, ocorrer o convite a qualquer dos alunos não escolhidos para que façam essa apresentação.

Todos os alunos que completaram o experimento devem preparar para a apresentação:

Os programas exemplo e modificados;

A introdução,

Os resultados,

A análise dos resultados, e

A conclusão.

Durante a apresentação deverão ser respondidas perguntas do Professor e de colegas.