O problema do produtor-consumidor é um clássico problema que aborda a sincronização de múltiplos processos.
Este problema consiste de dois processos, o produtor e o consumidor, que compartilham uma memória (buffer). O produtor é responsável por gerar dados e colocá-los no buffer, repetindo estas ações inúmeras vezes. Enquanto isso, o consumidor remove/consome dados do buffer. Como o buffer possui um tamanho limitado, devemos garantir que o processo produtor não irá adicionar dados em um buffer cheio e que o consumidor não irá remover dados de um buffer vazio.
A solução para este problema consiste em colocar os processos para dormir enquanto uma das condições de parada valer. Se o buffer estiver cheio, o produtor dorme. Se o buffer estiver vazio, o consumidor dorme.
Um dos processos é responsável por acordar o outro processo. O produtor acorda o processo consumidor quando inserir dados no buffer; e o consumidor acorda o processo produtor quando remover dados do buffer.
Este problema torna-se interessante, pois soluções inadequadas resultam em um deadlock, ou seja, ambos processos estão dormindo.
Para aprender mais sobre este problema, visite: http://en.wikipedia.org/wiki/Producers-consumers_problem
Em nossa solução, podemos definir a quantidade de processos produtores e a quantidade de processos consumidores teremos. Definimos também a quantidade de pacotes que os produtores criarão e a capacidade máxima do pipe. Para criar os processos filhos, devemos utilizar a instrução fork() - http://www.opengroup.org/onlinepubs/000095399/functions/fork.html
Para produzir um novo produto, cada produtor levará um tempo randômico e para consumir cada produto, cada consumidor levará um tempo também randômico.
Além disso, se desejamos escrever no pipe, devemos fechar a ponta de leitura. E, se desejamos ler do pipe, devemos fechar a ponta de escrita.
Maiores informações podem ser obtidas em: http://www.cs.cf.ac.uk/Dave/C/node23.html
Para compilar o programa e gerar o executável, utilizaremos:
gcc [[nome_programa.c]] -o [[nome_programa]] -lpthread
Para executar o programa criado, utilizaremos:
./[[nome_programa]]
Utilizamos o seguinte código:
//bibliotecas utilizadas...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//define os lados de leitura e escrita dos pipes...
#define READ 0
#define WRITE 1
//define a quantidade de produtos produzidos pelos produtores e a quantidade de pacotes que um pipe suporta
#define MAX_PROD_PACKS 100
#define MAX_PIPE_PACKS 30
//define a quantidade de produtores e de consumidores
#define PRODUCERS 2
#define CONSUMERS 2
//definimos uma estrutura para os produtos a serem produzidos, armazenando o id do pai
typedef struct prod prod;
struct prod{int prodid;pid_t producerid;};
int fd[2];
int n;
//Prototipos...
void consume_prod(prod*);
prod produce_prod(int);
void producer()
{
int qtdade;
prod newprod;
close(fd[READ]); // Fecha o lado de leitura. Lado nao utilizado
for(qtdade=0;qtdade<MAX_PROD_PACKS;qtdade++){
newprod = produce_prod(qtdade); //produz novo item
printf("Novo item produzido pelo processo %d! \n", getpid());
write(fd[WRITE],&newprod,sizeof(prod)); // insere novo item no pipe
}
close (fd[WRITE]); // Fecha o lado utilizado
}
void consumer()
{
int qtdade;
int resp;
prod consumeprod;
close (fd[WRITE]); /* Fecha o lado de leitura que nao eh utilizado*/
while( 1 ){
resp = read(fd[READ],&consumeprod,sizeof(prod));
if(resp==-1){
printf("Erro na leitura do pipe\n");
}
else if(resp==0){
printf("Pipe estah vazio... \n");
}
else{
consume_prod(&consumeprod);
}
}
close (fd[READ]); /* Fecha o lado utilizado*/
}
prod produce_prod(int n){
prod newprod;
int timetoproduce;
timetoproduce = rand()%7;
newprod.prodid = n;
newprod.producerid = getpid();
printf("Processo %d produzindo um novo produto\n",getpid());
sleep(timetoproduce);
return newprod;
}
void consume_prod(prod * t){
int timetoconsume;
timetoconsume=rand()%5;
printf("Processo %d consumindo produto %d do Produtor %d\n",getpid(),t->prodid,t->producerid);
sleep(timetoconsume);
}
void new_producer(){
int newprocess;
newprocess = fork();
if(newprocess==-1){
printf("Erro na criacao do processo produtor\n");
}
else if(newprocess==0){
producer();
exit(0);
}
return;
}
void new_consumer(){
int newprocess;
newprocess = fork();
if(newprocess==-1){
printf("Erro na criacao do processo consumidor\n");
}
else{
consumer();
exit(0);
}
}
int main(){
pipe(fd); //cria o pipe
int i;
/* Criação de Processos Produtores */
for( i=0;i<PRODUCERS;i++){
new_producer();
}
/* Criação de Processos consumidores */
for(i=0;i<CONSUMERS;i++){
new_consumer();
}
//o processo pai será um processo consumidor
exit(0);
}
Assim, obtivemos o seguinte resultado:
andersonaiziro@Aaiziro:~/Documents$ ./prod-consProcesso 6743 produzindo um novo produto
Processo 6744 produzindo um novo produto
Novo item produzido pelo processo 6743!
Processo 6743 produzindo um novo produto
Processo 6745 consumindo produto 0 do Produtor 6743
Novo item produzido pelo processo 6744!
Processo 6744 produzindo um novo produto
Processo 6746 consumindo produto 0 do Produtor 6744
Novo item produzido pelo processo 6743!
Processo 6743 produzindo um novo produto
Processo 6746 consumindo produto 1 do Produtor 6743
Novo item produzido pelo processo 6744!
Processo 6744 produzindo um novo produto
Processo 6742 consumindo produto 1 do Produtor 6744
Novo item produzido pelo processo 6743!
Processo 6743 produzindo um novo produto
Processo 6746 consumindo produto 2 do Produtor 6743
Novo item produzido pelo processo 6744!
Processo 6744 produzindo um novo produto
Processo 6745 consumindo produto 2 do Produtor 6744
Novo item produzido pelo processo 6743!
Processo 6743 produzindo um novo produto
Processo 6746 consumindo produto 3 do Produtor 6743
Novo item produzido pelo processo 6744!
Processo 6744 produzindo um novo produto
Processo 6745 consumindo produto 3 do Produtor 6744
Novo item produzido pelo processo 6743!
Processo 6743 produzindo um novo produto
Processo 6746 consumindo produto 4 do Produtor 6743
Novo item produzido pelo processo 6744!
Processo 6744 produzindo um novo produto
Processo 6742 consumindo produto 4 do Produtor 6744
Novo item produzido pelo processo 6743!
Processo 6743 produzindo um novo produto
Processo 6742 consumindo produto 5 do Produtor 6743
Novo item produzido pelo processo 6744!
Processo 6744 produzindo um novo produto
Processo 6746 consumindo produto 5 do Produtor 6744
Novo item produzido pelo processo 6743!
Processo 6743 produzindo um novo produto
Processo 6742 consumindo produto 6 do Produtor 6743
Novo item produzido pelo processo 6744!
Processo 6744 produzindo um novo produto
Processo 6746 consumindo produto 6 do Produtor 6744
Novo item produzido pelo processo 6743!
Processo 6743 produzindo um novo produto
Processo 6746 consumindo produto 7 do Produtor 6743
Novo item produzido pelo processo 6744!
Processo 6744 produzindo um novo produto
Processo 6745 consumindo produto 7 do Produtor 6744
Novo item produzido pelo processo 6743!
Processo 6743 produzindo um novo produto
Processo 6742 consumindo produto 8 do Produtor 6743
Novo item produzido pelo processo 6744!
Processo 6744 produzindo um novo produto
Processo 6745 consumindo produto 8 do Produtor 6744
Novo item produzido pelo processo 6743!
Processo 6743 produzindo um novo produto
Processo 6746 consumindo produto 9 do Produtor 6743
Novo item produzido pelo processo 6744!
Processo 6744 produzindo um novo produto
Processo 6745 consumindo produto 9 do Produtor 6744
Novo item produzido pelo processo 6743!
Processo 6743 produzindo um novo produto
Novo item produzido pelo processo 6744!
Processo 6744 produzindo um novo produto
Processo 6742 consumindo produto 10 do Produtor 6743
Processo 6742 consumindo produto 10 do Produtor 6744
Verificado o funcionamento do programa, vamos criar os processos zumbis!
Para realizar tal tarefa, vamos inicialmente, identificar uma situação em que temos os processos zumbis. Ao realizar o comando fork(), criamos um processo filho. A morte do processo pai, acarreta na morte do processo filho. Por outro lado, a morte do processo filho deve ser informada ao processo pai. Se o processo pai estiver bloqueado, então o processo filho torna-se um processo zumbi.
Para fazer esta situação acontecer, vamos fazer o processo pai como um processo consumidor e vamos criar uma situação em que não temos mais produtos no pipe. Assim, o processo pai ficará bloqueado e o processo filho não conseguirá informar ao pai sobre seu término, tornando-se um processo zumbi.
Para verificar a situação dos processos, vamos utilizar o seguinte comando:
ps -ax
O resultado desta simulação foi:
A existência de processos zumbi é indicada pela letra Z+.
Nenhum comentário:
Postar um comentário