Esta é a segunda parte da postagem sobre Pipes e Fifos aqui do blog. Se ainda não viu a primeira parte, pode acessá-la por aqui. Nesta parte será explicado como processos podem ter seus dados interligados, exatamente como o shell faz no comando ls | wc -l. Para entender como este processo ocorre é necessário entender que existem três "arquivos" que são abertos quando um processo se inicia.
Estes arquivos são:- entrada padrão ou stdin (file descriptor zero)
- a saída padrão ou stdout (file descriptor um)
- saída de erros ou stderr (file descriptor dois)
Ao utilizarmos a função printf, a string é automaticamente enviada para a stdout, ou saída padrão. Ainda assim podemos utilizar a função fprintf ou até mesmo a função write para especificar para onde queremos enviar a string, podendo ser a stdout, stderr ou ainda um fd de um arquivo aberto pelo usuário.
Utilizando estes file descriptors, podemos fazer com que a saída de um processo seja direcionado para a entrada de outro processo, exatamente como se estivéssemos colocando um "cano" entre os processos. O código abaixo, mostra um exemplo deste redirecionamento ao executar a função ls e direcionar a saída para outro processo. Algumas partes são comuns do exemplo do último artigo, pois o mesmo mecanismo de pipes é utilizado:
Este programa executa duas chamadas fork, sendo cada uma delas para criar um dos processos que serão conectados pelo pipe. O código abordado possui muitas chamadas de sistema relacionadas a arquivos, e todos estes serão explicados agora: fds[1] != STDOUT_FILENO Esta validação é necessária para validar se o stdout não foi previamente redirecionado para o fd[1] por um outro programa que chame o programa atual, e funciona como programação defensiva. dup2(fds[1], STDOUT_FILENO) A chamada de sistema dup2 duplica o fd do primeiro parâmetro, fecha o fd do segundo parâmetro e reabre o fd do segundo parâmetro com as características do primeiro fd. Quando fechamos um arquivo no Linux e reabrimos outro, este tende a pegar o número do fd do arquivo que foi fechado. Neste ponto, ao usar o dup2 nós estamos tentando fazer com que o fd zero, que é usado como entrada padrão, seja o fd[1] do pipe. Ou seja, quando o programa executar um printf, os dados que seriam enviados a saída padrão (file descriptor zero) será enviado ao fd[1] (que na verdade é o novo fd zero). É um pouco confuso no início, mas o código abaixo tenta exemplificar este comportamento de reusar o número de fd:A execução do programa acima é: marcos@localhost: [exemplos_c] # ./show_fd_reuse fd aberto é: 3 novo fd é: 3
Assim podemos entender um pouco melhor como os sistemas *NIX usam seus fds. E outro detalhe: O fd número três é utilizado, pois os fds zero, um e dois já estão sendo utilizados por stdin, stdout e stderr.
execlp(“ls”, “ls”, (char *)NULL); Esta chamada de sistema carrega um novo programa no espaço de memória do processo atual. Após a chamada fork, um novo processo é iniciado, e este continua a executar do ponto onde foi criado, e então a chamada execlp é utilizada para carregar o novo processo, apagando o processo pai do espaço de memória do processo criado. Um detalhe importante: este novo processo terá sua saída padrão sendo o fd[1], e desta forma estaremos enviando toda a saída do comando ls para o outro processo que irá ser criado.
Após esse ponto será criado um outro processo que irá pegar a saída do comando ls executado anteriormente.
dup2(fds[0], STDIN_FILENO) execlp(“wc”, “wc”, “-l”, (char *)NULL) Primeiramente a entrada padrão do processo do novo processo criado será atribuída ao fds[0], que é a parte de leitura do pipe. Após redirecionar a entrada padrão para que esta leia do pipe, o novo processo então executa o execlp para carregar o programa wc, passando o parâmetro -l.
close(fds[0]) close(fds[1]) wait(NULL) wait(NULL) Após executar ambos processos, então é necessário esperar para que ambos os processos terminem. A chamada de sistema wait faz com que a execução do processo pare e espere pelo término de um processo filho. Como não é especificado qual processo filho desejamos parar, então é executada a chamada de sistemas duas vezes para esperar pelo término dos dois processos.
Após a execução do programa, pode-se comparar sua saída do comando ls | wc -l c no bash: [marcos@xfiles pipe_fifo]$ ls | wc -l 4
[marcos@xfiles pipe_fifo]$ ./pipe_connect 4
Espero que tenham gostado desta segunda parte da postagem. A próxima e última parte abordará Fifos. Assine nosso feed e se inscreva em nossas páginas nas redes sociais para saber em primeira mão quando um novo artigo sair. Até mais!