Thursday, May 7, 2009

O Problema do Produtor e Consumidor (com pipes)

Em ciência da computação, o problema do produtor-consumidor (também conhecido como problema do buffer limitado) é um exemplo clássico de problema de sincronização multi-processo.

O problema descreve dois processos, o produtor e o consumidor, que compartilham um buffer de tamanho fixo. O trabalho do produtor é gerar um dado, colocá-lo no buffer e repetir essa operação indefinidamente. Ao mesmo tempo, a tarefa do consumidor é consumir tais dados (i.e. removendo do buffer), um de cada vez.

O problema consiste em assegurar que o produtor não irá tentar adicionar dados no buffer quando este estiver cheio, e que o consumidor não tentará remover dados quando o buffer estiver vazio.

A solução para o produtor é dormir quando o buffer estiver cheio. Na próxima vez que o consumidor remover um item do buffer, ele irá acordar o produtor, que continuará a colocar dados no buffer. Da mesma forma, o consumidor dorme quando encontra o buffer vazio. Na próxima vez que o produtor adicionar um dado no buffer, ele acordará o consumidor.

O problema pode ser generalizado para múltiplos produtores e múltiplos consumidores.

Para esta experiência, foi resolvido o problema do produtor-consumidor apenas para 1 produtor e 1 consumidor. A implementação foi feita utilizando-se pipes, os quais possuem seu próprio buffer interno. Foi assumido que os pipes são thread-safe (i.e., garantem a exclusão mútua e eliminam condições de disputa) visto que isto é previsto, de acordo com o padrão POSIX.

Vale ressaltar que, como o tamanho padrão do buffer interno do pipe é de 5Kbytes (muito grande), não foi possível, através da execução do programa implementado, visualizar a ocorrência de buffer cheio, previsto no problema do produtor-consumidor. No entanto, assumiu-se que a implementação do pipe, de acordo com o padrão POSIX, lida com a situação de buffer cheio adequadamente.

A saída para o programa pode ser verificada na figura abaixo:

Figura1: Saída do Programa
(clique na figura para melhor visualização)

Código:

#include "stdio.h"
#include "stdlib.h"
#include "unistd.h"

#define READ 0
#define WRITE 1
#define TRUE 1

/* protótipos */

void
cria_item(int i);
void
consome_item(int i);
void
produtor(int fd[2], int qtde_itens);
void
consumidor(int fd[2]);

int
main (){
int
fd[2];

/* criando pipe */

pipe (fd);

/* criando novo processo */

int
pid = fork();

if
(pid == -1) {
perror("Erro ao criar um novo processo!");
}
else if (pid == 0) {
/* o novo processo funciona como produtor */

produtor(fd, 15);
}
else {
/* o processo pai funciona como consumidor */

consumidor(fd);
}


return
0;
}


void
produtor(int fd[2], int qtde_itens) {
close(fd[READ]);

int
i, bytesEscritos;
for
(i = 1 ; i <= qtde_itens; i++) {
sleep( rand() % 5 );
cria_item(i);

/* escreve no pipe */

bytesEscritos = write(fd[WRITE], &i, sizeof(int));

if
(bytesEscritos == -1) {
perror("Erro de escrita no pipe!");
}
}

close (fd[WRITE]);
}



void
consumidor(int fd[2]){
close (fd[WRITE]);

int
i, bytesLidos;
while
(TRUE) {
/* lê do pipe */

bytesLidos = read (fd[READ], &i, sizeof(int));
sleep( rand() % 5 );
consome_item(i);

if
(bytesLidos == -1) {
perror("Erro de leitura no pipe!");
}
else if (bytesLidos == 0) {
break
;
}
}

close (fd[READ]);
}


void
cria_item(int i){
printf("Produtor criou o %d.o item.\n", i);
}


void
consome_item(int i){
printf("Consumidor consumiu o %d.o item.\n", i);
}

Fontes:

http://en.wikipedia.org/wiki/Producer-consumer_problem
Sistemas Operacionais Modernos, 2a ed - A.S.Tanenbaum

No comments:

Post a Comment