quinta-feira, 7 de maio de 2009

Problema dos Produtores e Consumidores utilizando pipes

O Problema dos Produtores e Consumidores ocorre quando diversos processos de dois tipos (produtor e consumidor) compartilham um buffer para envio de mensagens. Os produtores são responsáveis por criar "objetos" e colocá-los no buffer. Já os consumidores retiram esses objetos do buffer. O buffer obedece o comportamendo de uma fila, "first in first out"
O problema ocorre quando dois produtores tentam colocar objetos no buffer ao mesmo tempo ou quando dois consumidores retiram objetos do buffer ao mesmo tempo. A solução deve ser capaz permitir somente um produtor adcionando objeto e um consumidor retirando objeto ao mesmo tempo.
Também é necessário gerenciar as situações em que o buffer estpa cheio, impossibilitando uma nova adição, e quando está vazio, impossibilitando que um objeto seja consumido.

Na implementação aqui apresentada, utilizou-se os pipes do padrão posix, que possui todos os requerimentos necessários para o buffer. Por ser thread-safe, ele possibilita somente que um processo esteja adicionando objeto ao pipe e somenque um que esteja consumindo. Também já gerencia os casos em que o buffer está vazio ou cheio.

Nessa solução são criados 10 produtores que adionam um objeto ao pipe, que corresponde ao index que identifica o produtor. Em seguida, são criados 5 consumidores e após 6 segundos mais 10 produtores. Os consumidores estão sempre lendo o pipe até que seja esvaziado e então o processo é finalizado.

Abaixo segue o código da solução:
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
#include<unistd.h>

#define LER 0 //indexa a leitura no pipe
#define ESCREVER 1 //indexa a escrita no pipe
#define PRODUTORES 10; // define numero de produtores
#define CONSUMIDORES 5; // define numero de leitores


int
fd [2];

void
produtor( int produtor);

void
consumidor(int consumidor);
void
criar_consumidor(int consumidor);

void
criar_produtor(int produtor);

main
()
{

int
j = 0;

pipe (fd);

//cria produtores
int i = PRODUTORES;
for
(j = 1; j <= i; j++)
{

criar_produtor(j);
}


//cria consumidores
i = CONSUMIDORES;
for
(j = 1; j<= i; j++)
{


criar_consumidor(j);
}


sleep(6); //espera 6 s para produzir mais
i = PRODUTORES;

for
(j = i +1 ; j <= 2*i; j++)
{

criar_produtor(j);
}



return
0;
}


void
produtor(int produtor_id)
{


int
bytes_escritos;


close(fd[LER]);


bytes_escritos = write (fd[ESCREVER],&produtor_id, sizeof(int));

if
(bytes_escritos == -1) { //se ocorre erro fecha processo
printf("\nProblema na escrita do produtor %d", produtor_id);
exit(-1);
}


else
{
printf("\nProdutor %d enviou mensagem", produtor_id); //conseguiu enviar mensagem
}

close (fd[ESCREVER]);

exit(0);


}


void
consumidor(int consumidor_id)
{


int
msg;
int
bytes_lidos;
int
max_zero;

while
(1){

sleep(1); //intervalo de 1 s entre as leituras

close (fd[ESCREVER]);
bytes_lidos = read (fd[LER],&msg, sizeof(int));


if
(bytes_lidos == 0){ //caso não tenha nada no pipe finaliza o processo
printf("\nConsumidor %d saiu", consumidor_id);

break
;
}

else if
(bytes_lidos == -1){//se ocorre erro na leitura, sai
printf("\nProblema na leitura do consumidor %d",consumidor_id );

exit(-1);
}

else
printf("\nConsumidor %d leu mensagem: %d com %d bytes\n", consumidor_id, msg, bytes_lidos);

}


close(fd[LER]);

exit(0);

}


void
criar_consumidor(int consumidor_id)
{


int
pid;
pid = fork ();
if
(pid == 0){//caso seja processo filho realiza operação de consumidor

consumidor(consumidor_id);
}
}


void
criar_produtor(int produtor_id)
{

int
pid;

pid = fork ();

if
(pid == 0){//caso seja processo filho realiza operação de produtor

produtor(produtor_id);
}
}


O resultado é mostrado abaixo:


Produtor 1 enviou mensagem
Produtor 2 enviou mensagem
Produtor 3 enviou mensagem
Produtor 4 enviou mensagem
Produtor 5 enviou mensagem
Produtor 6 enviou mensagem
Produtor 7 enviou mensagem
Produtor 8 enviou mensagem
Produtor 9 enviou mensagem
Produtor 10 enviou mensagem
Consumidor 1 leu mensagem: 1 com 4 bytes

Consumidor 2 leu mensagem: 2 com 4 bytes

Consumidor 3 leu mensagem: 3 com 4 bytes

Consumidor 4 leu mensagem: 4 com 4 bytes

Consumidor 5 leu mensagem: 5 com 4 bytes

Consumidor 1 leu mensagem: 6 com 4 bytes

Consumidor 2 leu mensagem: 7 com 4 bytes

Consumidor 3 leu mensagem: 8 com 4 bytes

Consumidor 4 leu mensagem: 9 com 4 bytes

Consumidor 5 leu mensagem: 10 com 4 bytes

Consumidor 1 leu mensagem: 11 com 4 bytes

Produtor 11 enviou mensagem
Consumidor 3 leu mensagem: 12 com 4 bytes

Produtor 12 enviou mensagem
Consumidor 2 leu mensagem: 13 com 4 bytes

Produtor 13 enviou mensagem
Consumidor 4 leu mensagem: 14 com 4 bytes

Produtor 14 enviou mensagem
Consumidor 5 leu mensagem: 15 com 4 bytes

Produtor 15 enviou mensagem
Produtor 16 enviou mensagem
Produtor 17 enviou mensagem
Produtor 18 enviou mensagem
Produtor 19 enviou mensagem
Produtor 20 enviou mensagem
Consumidor 1 leu mensagem: 16 com 4 bytes

Consumidor 3 leu mensagem: 17 com 4 bytes

Consumidor 2 leu mensagem: 18 com 4 bytes

Consumidor 4 leu mensagem: 19 com 4 bytes

Consumidor 5 leu mensagem: 20 com 4 bytes

Consumidor 1 saiu
Consumidor 3 saiu
Consumidor 2 saiu
Consumidor 4 saiu
Consumidor 5 saiu

O resultado mostra que nenhum consumidor obteve uma mensagem igual, que nenhuma mensagem foi perdida e que as ordem em que as mensagens chegaram foi a mesmo em que foram enviadas.

Com o comando "ps -aux" no console do linux, pode-se ver informações sobre os processos decorrentes do programa executado. Abaixo segue o output:

david 7524 0.0 0.0 3780 368 pts/2 S+ 04:24 0:00 ./prod
david 7525 0.0 0.0 0 0 pts/2 Z+ 04:24 0:00 [prod]
david 7526 0.0 0.0 0 0 pts/2 Z+ 04:24 0:00 [prod]
david 7527 0.0 0.0 0 0 pts/2 Z+ 04:24 0:00 [prod]
david 7528 0.0 0.0 0 0 pts/2 Z+ 04:24 0:00 [prod]
david 7529 0.0 0.0 0 0 pts/2 Z+ 04:24 0:00 [prod]
david 7530 0.0 0.0 0 0 pts/2 Z+ 04:24 0:00 [prod]
david 7531 0.0 0.0 0 0 pts/2 Z+ 04:24 0:00 [prod]
david 7532 0.0 0.0 0 0 pts/2 Z+ 04:24 0:00 [prod]
david 7533 0.0 0.0 0 0 pts/2 Z+ 04:24 0:00 [prod]
david 7534 0.0 0.0 0 0 pts/2 Z+ 04:24 0:00 [prod]
david 7535 0.0 0.0 3784 244 pts/2 S+ 04:24 0:00 ./prod
david 7536 0.0 0.0 3784 244 pts/2 S+ 04:24 0:00 ./prod
david 7537 0.0 0.0 3784 244 pts/2 S+ 04:24 0:00 ./prod
david 7538 0.0 0.0 3784 244 pts/2 S+ 04:24 0:00 ./prod
david 7539 0.0 0.0 3784 244 pts/2 S+ 04:24 0:00 ./prod
david 7541 0.0 0.0 7472 912 pts/1 S+ 04:24 0:00 grep prod

Pode-se notar que alguns processo estão em estado zoobie (Z+) e outros estão dormindo (+S). Os processos domindo são os consumidores que dormiram em virtude do comando sleep(), os demais podem ser processos filhos, tanto consumidores ou produtores, que estão esperando o proceso finalizá-los.

Nenhum comentário:

Postar um comentário