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);
}