Por que precisamos de dois descritores de arquivos ao criar um canal não nomeado?

4

Eu tenho lido sobre pipes não nomeados e como eu os entendi eles são implementados como um buffer na memória. Ao criar o pipe eu preciso passar uma matriz de tamanho dois e ele retorna dois ponteiros (descritores de arquivo) para o buffer. O índice 0 é usado para leitura do pipe e índice 1 para gravar nele.

Minha pergunta é, se o buffer é apenas um e ambos os índices apontam para a mesma localização de memória e dois processos não podem ler e escrever ao mesmo tempo, então por que eu preciso de dois descritores de arquivo? Espero que minha pergunta faça sentido.

    
por Charles 19.06.2013 / 13:56

2 respostas

5

Como afirmado por @MelBoyce em seu comentário, isso é por causa da natureza conceitual de um pipe. Ele tem uma entrada e uma saída e você lê os bytes da saída na mesma ordem em que foram gravados na entrada. Pipes não são arquivos ou ponteiros comuns, você não deve ler e escrever em qualquer lugar. Você é forçado a ler os primeiros bytes que entraram no pipe e que nunca serão lidos ainda.

Pipes podem ser implementados como um buffer na memória, mas a implementação pode variar se no futuro, se outra forma mais eficiente de fazer isso for inventada, por exemplo. No entanto, a natureza conceitual do pipe não será alterada. Você ainda usará as mesmas chamadas de sistema read(1) e write(1) e elas ainda se comportarão da mesma maneira. Os descritores de arquivo que você recebe quando chama pipe(1) são usados para forçá-lo a usar o canal corretamente (e adicionalmente fornecer algum controle de acesso). Dessa forma, futuras modificações na implementação não irão quebrar seu código.

    
por 19.06.2013 / 14:39
2

Um recurso importante estaria faltando se pipe retornasse um único descritor de arquivo a ser usado para leitura e escrita: não haveria como sinalizar EOF.

Com os pipes como estão, a leitura fd vê EOF quando a última cópia do write fd é fechada. Com os tubos hipotéticos de fd simples, você precisaria de um syscall extra, como shutdown para soquetes, mas aplicado a um pipe. (Lembre-se que os pipes são mais antigos que os soquetes e shutdown não existia antes dos soquetes.)

E quem seria responsável por chamar este pipe_shutdown() syscall? Suponha que você faça isso:

grep foo /etc/passwd | head

Se houver apenas um único fd, que grep e head herdaram, então, de alguma forma, quando grep sair, head precisará receber um EOF. Mas o fd mantido pelo processo head é gravável, então o kernel não tem certeza de que nada mais será gravado no pipe. Você pode estar pensando: a saída do processo deve enviar automaticamente o EOF em todos os fds do pipe mantidos pelo processo. Mas isso quebrará muitas coisas, como

( echo FOO ; grep foo /etc/passwd ) | more

echo grava no pipe e sai, e não queremos que um EOF aconteça ainda. grep ainda vai gravar no pipe. E esse problema não está limitado ao caso "obscuro" de subshells entre parênteses. Isso pode acontecer a qualquer momento em que um script de shell é usado no lado esquerdo de um pipe.

Se você pensar sobre isso, o mecanismo EOF do pipe realmente requer uma distinção entre fds que podem gravar no pipe e fds que só podem lê-lo. E isso não depende de quantos processos estão envolvidos.

    
por 20.06.2013 / 00:06