Você está na página 1de 5

Pipes em C - Sincronismo entre

processos com mais de um pipe


Em nosso tutorial passado, sobre Pipes em C, falamos dessa importante ferramenta para a
troca de informações e dados entre processos em um Sistema Operacional.
Agora, iremos entrar em mais detalhes sobre o uso de pipes em C.

Pipes e Sincronismo entre Processos


No exemplo passado, fizemos o uso bem básico e simples do pipe, que foi de enviar uma
informação de um processo para outro, através das chamadas de sistema write e read.

Agora, vamos fazer algo um pouco mais complexo e interessante: não vamos somente
enviar a informação em um sentido (escrevendo de um lado, e lendo de outro), vamos fazer
com que um processos escreva e leia, e o outro também.

Vamos resolver a seguinte questão:


Crie um programa em C onde o processo pai cria um processo filho.
No processo pai, será pedido dois números inteiros, que deverão ser enviados ao filho via
pipe, e este vai fazer a soma e devolver o resultado desta operação para o pai, e tudo se
repete.
Isso deve ocorrer indefinidamente.

A grande dificuldade desse tipo de evento, é o sincronismo.


Ou seja, existem estágios. Em alguns momentos um processo vai escrever algum dado, e o
outro não deve fazer nada, além de esperar.

Depois ocorre o contrário, o processo que esperava é que vai escrever, e o outro é que tem
que esperar. Isso é o sincronismo entre pipes, saber quando cada processo deve agir.

Usando dois Pipes para trocar


informações
No exemplo do tutorial passado sobre o uso de pipes em C, as informações fluíam apenas
em um sentido do pipe. Ou seja, um processo escrevia e o outro lia.

Na questão proposta, isso também ocorre.


Bem como o contrário: o pai escreve e o filho lê, depois é o filho que escreve e o pai que lê.

Uma solução para este tipo de situação, bem corriqueira em sistemas operacionais, é através
do uso de dois pipes. Veja a imagem que ilustra essa solução:

Uso de dois pipes para troca de informações entre processos


Temos dois pipes, que são representados pelos files descriptors fd1 e fd2.
O pai vai escrever no Pipe 1, o fd1 e vai ler pelo Pipe 2, o fd2.
Já o filho vai ler do Pipe 1 e escrever no Pipe 2.

Ou seja, agora ambos processos podem enviar e receber informações do outro.


Vamos ver como passar essa ideia para código (em C, claro ;)

Como usar dois Pipes em C


O grande cuidado que devemos ter quando estamos trocando informações entre processos
usando mais de um pipe é saber o momento em que cada coisa vai fazer algo, ou seja, a
sincronia.
No próximo item deste tutorial, temos o código em C pronto, comentando e bem
organizado.
Agora vamos explicá-lo!

O grande 'segredo' da coisa é a variável 'turn', existente em cada processo, que começa com
valor 0.
Turn em inglês quer dizer turno, vez, rodada...ou seja, essa variável vai ditar as regras sobre
o que cada processo deve fazer em cada momento.

Depois da criação dos pipes e do uso da chamada de sistema fork(), entramos no processo
pai.
Aqui, declaramos o vetor de inteiros num que vai armazenar os dois inteiros do usuário, e o
inteiro soma que vai receber o valor da soma dos filhos.

Em seguida devemos fechar os 'lados' do pipe que não vamos usar no processo pai, pela
figura acima vemos que é o fd1[0] e o fd2[1], através da função close.

Agora o processo pai e o filho entram em looping infinito, definido por while(1).
Vamos definir dois estados, que ditarão as regras do sincronismo.

O primeiro estado é quando turn=0, aqui vai acontecer duas coisas: o pai vai pedir os
números ao usuário e o filho vai esperar por esses dados.

Os números enviados pelo pai estão em um vetor, então basta passarmos o nome desse
vetor e o número de bytes que queremos passar pela função write(), este número é o
tamanho da variável num (que é calculado por sizeof(num) ).
Quando o pai termina essa tarefa, ele passa o estado para turn=1

Ainda no filho, em turn=0, ele vai receber um vetor e vai fazer a sua variável numeros,
que é um ponteiro, apontar para esse vetor que recebeu pela função read, e vai ler
sizeof(numeros) bytes.
Em suma: ele vai receber o vetor de inteiros com duas posições, pois foi isso que o pai
passou.
Agora que recebeu, também muda seu estado para turn=1

Ok, o pai enviou e o filho recebeu, essa primeira parte já foi. Agora é turn=1
Neste passo, o filho calcula a soma dos dois números (na qual o vetor numeros aponta) e
manda esses eles via função write para o pipe. No fim do processo filho, ele volta para
turn=0, para que tudo posso ocorrer de novo.

Do outro lado do pipe, ainda no estágio turn=1, o processo pai fica esperando um inteiro
através do pipe, usando a read.
Assim que recebe, armazena esse inteiro em sua variável soma, que é um inteiro.
Por fim, o pai exibe a soma e retorna para o valor turn=0, para que o processo volte do
começo.

Código comentado em C do uso de Pipes Sincronizados


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(void) {
int fd1[2], /* Pai vai escrever e Filho ler por esse file
descriptor */
fd2[2], /* Pai vai ler e o Filho escrever por esse file
descriptor */
turn=0; /* Vai definir o que cada um vai fazer (ler,
escrever, aguardar...) */
pid_t pid; /* Armazena o pid, para o tratamento de pai e filho
*/

/* Cria o pipe 1 */
if(pipe(fd1)<0) {
perror("pipe") ;
return -1 ;
}
/* Cria o pipe 2 */
if(pipe(fd2)<0) {
perror("pipe") ;
return -1 ;
}

/* Cria processo filho. */


pid = fork();

if(pid == -1) {
perror("fork") ;
return -1 ;
}

if(pid > 0) { /* Processo pai*/


int num[2], /* Números que o processo pai lê*/
soma; /* Resultado da soma, recebido pelo filho*/

/* Fechando o descritor LEITURA no primeiro pipe. */


close(fd1[0]);
/* Fechando o descritor ESCRITA no segundo pipe. */
close(fd2[1]);
while(1)
if(turn==0){ /* Pai vai escreever */
printf("Insira o numero 1: "); scanf("%d", &num[0]);
printf("Insira o numero 2: "); scanf("%d", &num[1]);

write(fd1[1], num, sizeof(num)); /* Enviando o vetor


de números pro filho */
turn=1; /* Passa para o próximo passo, que é o pai ler
a soma do filho */
}else

if(turn==1){ /* Pai vai ler a soma */


read(fd2[0], &soma, sizeof(soma)); /* Pai leu o
resultado da soma, e armazenou no inteiro 'soma' */
printf("Soma: %d\n\n", soma);
turn=0; /* Retorna pro passo anterior, pra começar
tudo de novo */
}

close(fd2[0]);
close(fd1[1]);

} else {
int numeros[2],
soma;

/* Fechando o descritor ESCRITA no primeiro pipe. */


close(fd1[1]);
/* Fechando o descritor LEITURA no segundo pipe. */
close(fd2[0]);

while(1){
if(turn==0){ /* Filho vai ler o vetor de numeros do pai */
read(fd1[0], numeros, sizeof(numeros) ); /* Recebeu o
vetor de inteiros do pai e colocou no vetor 'numeros' */
turn=1; /* Passa para o próximo passo, que é o filho
somar e escrever o resultado da soma */
}else

if(turn==1){ /* Filho calcula a soma e retorna pro pai */


soma = numeros[0] + numeros[1];

write(fd2[1], &soma, sizeof(soma)); /* Envia a soma,


qúe está na variável 'soma', para o pai */
turn=0; /* Volta para o passo anterior, que é esperar
vetor de inteiros do pai */
}
}

close(fd2[1]);
close(fd1[0]);
}

return 0 ;
}

Veja o programa funcionando:


Programa que usa dois pipes de maneira sincronizada