Sim, um pipe feito com pipe () possui dois descritores de arquivo. fd[0]
para leitura e fd[1]
para escrita.
Não, você não precisa fechar nenhuma das extremidades do pipe, ele pode ser usado para comunicação bidirecional.
Editar: nos comentários você quer saber como isso se relaciona com ls | less
, então vou explicar isso também:
Seu shell tem três descritores de arquivos abertos: 0 (stdin), 1 (stdout) e 2 (stderr). Quando um shell executa um comando, ele faz algo assim (simplifiquei um pouco:
pid = fork();
if(pid == 0) {
/* I am the child */
execve(...); /* Whatever the user asked for */
}
else {
waitpid(pid); /* Wait for child to complete */
}
Os descritores de arquivo 0, 1 e 2 são herdados pelo filho, portanto, a entrada / saída funciona conforme o esperado. Se você faz ls | pipe
, algo ligeiramente diferente acontece para fazer o redirecionamento:
int pipe[2];
pipe(pipe, 0);
pid1 = fork();
if(pid1 == 0) {
/* This is ls, we need to remap stdout to the pipe. We don't care about reading from the pipe */
close(pipe[0]);
close(1);
dup2(pipe[1], 1);
execve(...);
}
else {
pid2 = fork();
if(pid2 == 0) {
/* This is less, it reads from the pipe */
close(pipe[1]);
close(0);
dup2(pipe[0], 0);
execve(...);
}
else {
waitpid(pid1);
waitpid(pid2);
}
}
Assim, o shell cria o pipe, os garfos e, pouco antes de executá-lo, remapeia o pipe para stdin ou stdout dos processos filhos, fazendo com que o fluxo de dados seja do processo um para o process 2. Como os shell pipes não são bidirecionais, eles usam uma extremidade do pipe e feche a outra extremidade (ela realmente fecha a outra extremidade também, depois de duplicar o filedescriptor para stdin ou stdout).