Pipes

Um pipe pode ser considerado um canal de comunicação que liga dois processos e permite um fluxo de informação unidireccional (half-duplex). Do ponto de vista do programador, o interface usado é igual aos ficheiros(os descritores de um pipe são idênticos aos dos ficheiros, motivo pelo qual as primitivas de leitura e escrita são os habituais read e write).

Um pipe é representado por um array de inteiros de duas posições (posição 0 - Leitura ; posição 1 - Escrita).

 

FILE *popen(const char *comando, const char *modo) - permite que um programa execute outro sendo possível enviar ou receber dados desse programa. O modo pode ser r (leitura) ou w (escrita).

Se o modo for de leitura, o programa actual pode ler os dados de saída do programa executado através das primitivas disponibilizadas pela biblioteca stdio.h (ex: fread,fscanf). Se o modo for de escrita, o programa pode enviar dados para o programa invocado através das mesmas funções (ex: fwrite,fprintf ).

int pclose(FILE *apontador) - fecha o ficheiro.

int pipe(int descritor[2]) - cria um pipe. A função devolve dois descritores ( um representa a extremidade de escrita - descritor[1] e outro representa a extremidade de leitura - descritor[0] ). Para operações de leitura ou escrita são utilizadas as funções read e write.

int dup(int canal) - duplica o canal de comunicação <canal> (um descritor de ficheiros). O sistema operativo percorre a tabela de descritores de ficheiros à procura de uma posição livre. Quando obtém sucesso, duplica nessa posição o descritor de ficheiros <canal>, devolvendo essa posição.

int dup2(int descritor1, int descritor2) - associa ao descritor de ficheiros <descritor2> o mesmo ficheiro que o <descritor1> referencia.

int read(int descritor, buffer, int n_bytes) - lê do ficheiro apontado por <descritor>, n_bytes, para o buffer <buffer>. Retorna o número de bytes lidos com sucesso.

int write(int descritor, buffer, int n_bytes) - grava no ficheiro apontado por <descritor>, n_bytes, do buffer <buffer>. Retorna o número de bytes gravados com sucesso.

close(descritor) - fecha o ficheiro apontado por <descritor> (é libertada uma entrada da tabela de descritores de ficheiros).

 

Observações:

 

Exemplo 1:

 

#include <stdio.h>

 

/* Este programa demonstra a funcionalidade da função popen */

 

main()

{

int d;

FILE *executa; /* este descritor vai ser usado para ler a "saída" da

execução do comando "sort fich.txt" */

 

char buffer[81];

int i;

executa=popen("sort fich.txt","r"); /* permite ler o resultado de

executar o comando "sort fich.txt" */

 

printf("Vou executar o comando 'sort fich.txt' e apresentar o resultado\n");

/* Enquanto existem dados para ler, lê esses dados e imprime-os no monitor */

while((d=fread(buffer,sizeof(char),80,executa)))

{

buffer[d]='\0';

printf("%s",buffer);

}

pclose(executa); /* fecha o descritor */

 

}

 

 

Exemplo 2:

 

#include <unistd.h>

#include <sys/types.h>

#include <sys/wait.h>

 

/* Este programa demonstra a utilização dos pipes, que permitem a comunicação entre processos */

 

main()

{

int estado, dados[2]; /* array utilizado para criar o pipe

dados[0] - permite ler dados do pipe

dados[1] - permite escrever dados no pipe */

 

pid_t pid;

char res[50],buffer[50]= "Primeira experiencia com pipes";

pipe(dados); /* Cria o pipe - isto é feito antes de criar o processo

filho, para possibilitar que o filho e o pai tenham os

mesmos descritores para o pipe - que permite a

comunicação entre os processos */

pid=fork();

if (pid<0)

{

printf("Erro\n");

exit(-1);

}

else

if (pid)

{ /* Como o pai só vai escrever, fecha o pipe para leitura. */

close(dados[0]);

 

/* Escreve o buffer no pipe */

write(dados[1],buffer,sizeof(buffer)+1);

close(dados[1]);

wait(&estado);

 

}

else

{ /* Como o filho só vai ler, fecha o pipe para escrita */

close(dados[1]);

 

/* Lê dados do pipe */

read(dados[0],res,sizeof(res)+1);

printf("O filho leu do pipe a string %s\n",res);

close(dados[0]);

}

}

 

 

 

Exemplo 3:

 

 

#include <string.h>

#include <unistd.h>

#include <sys/wait.h>

 

/* Este exemplo mostra a utilização de pipes e o redireccionamento, que permite que a saida ou entrada de um pipe seja redireccionada

 

Neste exemplo o processo pai vai receber a saida do comando "sort fich.txt" que é executado no processo filho – a saída do processo filho é redireccionada para o pipe*/

 

 

main()

{

int dados[2], res, estado, d;

pid_t pid;

char buffer[81];

 

if (pipe(dados)==0) /* cria o pipe */

{

if ((pid=fork()==0))

{

/* Processo filho */

 

/* #################################################################*/

 

/* A tabela de descritores do processo filho tem o seguinte:

 

Posição Comentário

0 STANDARD INPUT

1 STANDARD OUTPUT

2 STANDARD ERROR

3 dados[0]

4 dados[1]

… */

/* #################################################################*/

close(dados[0]);

 

 

/* #################################################################*/

/* Copia o descritor de escrita do pipe para o descritor de saída do processo – que é o STANDARD OUTPUT (1) */

/* #################################################################*/

dup2(dados[1],1);

 

 

/* #################################################################*/

 

/* A tabela de descritores depois das duas últimas linhas terem sido executadas, tem o seguinte:

Posição Comentário

0 STANDARD INPUT

1 dados[1]

2 STANDARD ERROR

3 Livre

4 dados[1] – que vai ser fechado na linha seguinte */

/* #################################################################*/

close(dados[1]); /* Fecha o STANDARD OUTPUT do filho – a saída do processo filho vai ser "escrita" no pipe */

 

execlp("sort","sort","fich.txt",0);

}

else /* PAI */

{

close(dados[1]);

/* Lê os dados do pipe */

while((d=read(dados[0],buffer,81)))

{

buffer[d] = ‘\0’;

printf("%s",buffer);

}

 

wait(&estado);

}

}

}

 

Exercícios

 

1 - Faça um programa que execute o comando sort e envie para esse comando as linhas de um ficheiro de texto (espera-se como resultado que as linhas fiquem ordenadas).

 

2 - Faça um programa que execute o comando "ps -ax" e escreva o resultado desse comando no monitor. Nota: Para ler os dados utilize um buffer com tamanho máximo de 80 caracteres.

 

3 - Faça um programa que crie um processo e:

Feche todos os descritores de ficheiros não utilizados.

 

4 - Dados dois arrays de inteiros, de 1000 elementos cada, faça um programa que utilize 5 processos filho para fazer a sua soma e guarde o resultado num array final (no processo pai). Utilize apenas um pipe e sinais para resolver este exercício.

 

5 - O mesmo que o exercício 4, mas agora utilize apenas pipes.

 

6 - Faça um programa que crie um processo e:

Como resultado, espera-se que se obtenha o mesmo que o comando "od fich.txt".

Nota: não utilize a função popen.

Sugestão: redireccione o Standard Input para ler dados do pipe. Utilize as funções exec.

  

7 - Considere o seguinte programa:

     injecta programa1 programa2 fich.txt

 

Codifique o programa “injecta” de modo a que o resultado do programa1 seja injectado no programa2 e gravado no ficheiro fich.txt.

Utilize pipes e funções exec.

  

8 - Uma empresa de computadores tem o registo das vendas efectuadas no ano anterior e pretende ter conhecimento dos registos em que foram vendidos mais de 20 computadores. Cada registo com mais de 20 computadores deve ser armazenado no array MAIORES. Como a empresa tem 50000 registos, encarregou-o de optimizar esta tarefa através da utilização de 10 processos, sendo cada um responsável pela pesquisa de 5000 registos.

 Nota 1:Tenha em atenção que o resultado desta pesquisa deve ser armazenado no processo principal.

Nota 2: Considere que cada registo é do tipo:

typedef struct {

      int codigo_cliente, quantidade;

      double preco_custo, preco_venda;

} registo;

 Nota 3: Apenas pode utilizar um pipe

 Utilize as primitivas de processos, pipes  e sinais.

 

9 - Através da utilização dos processos, funções exec e pipes, obtenha a mesma funcionalidade da execução da seguinte linha de comandos:

ls –la | sort | wc –l

 

10 - Qual é o objectivo do seguinte programa? Indique, justificando,  os valores apresentados em cada um dos processos (Assuma que as  funções utilizadas nunca “falham”).

void main()

{

   int   i , estado , pid;  

   int   fd[2]; 

   int   erro;

   char mens[100],ler[100];

    pipe (fd);

   dup2(fd[0], 0);

   dup2(fd[1], 1);

   close(fd[0]);

   close(fd[1]);

   for (i = 1; i < 5;  i++)

   {

      pipe (fd);

      pid = fork();

      if (pid > 0)   

          erro = dup2(fd[1], 1);

      else

          erro = dup2(fd[0], 0);

      close(fd[0]);

      close(fd[1]);

      if (pid)

         break;

   }

   sprintf(mens,"Este é o processo  %d com  a identificação %d e o seu pai é %d\n",   i, (int)getpid(), (int)getppid());

   write(1,mens,strlen(mens)+1);

   read(0,ler,sizeof(mens));

    wait(&estado);

   fprintf(stderr,"Processo actual = %d, dados =%s",(int)getpid(),ler);

   exit (0);

}