Suponhamos que você deseja implementar seu proprio shell. Uma das tarefas mais difícil que você terá será para implementar o pipe.
Pipe é o redirecinamento da saída padrão de um programa para a entrada padrão de outro.[http://pt.wikipedia.org/wiki/Pipe]
Este Artigo demonstra como isso pode ser feito de um forma interativa(usando for, while ou do…while) e não recursiva.
Comunicar 2 processos irmão
A primeira coisa a se entender é como comunicar irmãos. Uma boa alternativa é criar um tunel no pai e os filhos herdaram esse tunel. Pra criar tunel, precisamos da chamada ao sistema chamada pipe. essa chamada ao sistema recebe um vetor de inteiro e depois de executada na posição 0 conterá um descritor de Stream que pode apenas ser lido e na posição 1 conterá um descritor onde pode-se escrever.
…
int fd[2];
pipe(fd);
if(!fork()){
close(fd[1]);
…
}
else if(!fork()){
close(fd[0]);
…
}
else {close(fd[0]); close(fd[1]);}
Nesse caso o primeiro processo criado lerá o que o segundo escrever nesse tunel criado.
Fazer pipe entre 2 processos irmãos
No exemplo passado, apenas fizemos um tunel entre os processos, mas a comunicação ainda não é feita pela saida padrão para a entrada padrão. Para isso usaremos a função dup2 que é uma implementação da chamada ao sistema dup. A função dup2 recebe o identificador de 2 descritores e ela faz uma copia do primeiro no lugar do segundo.
…
int fd[2];
pipe(fd);
if(!fork()){
close(fd[1]);
dub2(fd[0], 0);//0 é o identificador do fluxo STDIN
}
else if(!fork()){
close(fd[0]);
dub2(fd[1], 1);//1 é o identificador do fluxo STDOUT
…
}
else{close(fd[0]); close(fd[1]);}
Neste caso, o primeiro processo lerá através da entrada padrão a saida padrão do segundo processo.
Fazer pipe entre n processos
Já conseguimos fazer o pipe entre 2 processos, mas muitas vezes precisamos fazer pipes entre vários processos, um exemplo é:
$ cat arquivo | grep palavra | sort | more
Nesse exemplo ele mostrará na tela todas as linhas do arquivo que contenha um certa palavra, de forma ordenada e com pausas quando a tela estiver cheia.
Isso só prova a necessidade de uma interação no código. Abaixo mostro o código onde consegui fazer isso.
int fd[2];//contem os tuneis
int past;//contem a saida do tunel anterior
int i=0;
for(; i<cmd.size()-1; i++){
pipe(fd);//cria o tunel que é herdado pelo filho
if(!fork()){//para o novo processo
if(i!=0){//se não for o primeiro comando
dup2(past, 0);//recebe como entrada a saida do tunel passado
}
dup2(fd[1], 1);//a saida vai para o novo tunel
execvp(cmd[i][0],cmd[i]);//especializa o novo processo
}
close(fd[1]);//fecha o tunel para o pai
if(i!=0) close(past);//fecha o tunel passado para o pai
past = fd[0];//o tunel atual agora é o passado
}
if(!fork()){//executado para o ultimo processo
if(i!=0){//se ele tb não for o primeiro
dup2(past, 0);//coloca a entrada para a saida do tunel
}
execvp(cmd[i][0],cmd[i]);//especializa este processo
}
Obs: cmd é um vector de arrays de arrays de characters(Declaração vector<**char> ou vector<char[] [] > )