Canais nomeados, descritores de arquivo e EOF


10

Duas janelas, mesmo usuário, com prompts do bash. Na janela 1, digite:

$ mkfifo f; exec <f

Portanto, o bash agora está tentando ler o descritor de arquivo 0, que é mapeado para o pipe nomeado f. Na janela 2, digite:

$ echo ls > f

Agora a janela 1 imprime um sl e o shell morre. Por quê?

Próxima experiência: abra a janela-1 novamente com exec <f. Na janela 2, digite:

$ exec 3>f
$ echo ls >&3

Após a primeira linha acima, a janela 1 é ativada e imprime um prompt. Por quê? Após a segunda linha acima, a janela 1 imprime a lssaída e o shell permanece ativo. Por quê? De fato, agora na janela 2, echo ls > fnão fecha o shell da janela 1.

A resposta deve ter a ver com a existência do descritor de arquivo 3 da janela 2, referenciando o pipe nomeado ?!


1
Depois exec <f, bashnão está tentando ler a partir f, é primeira tentativa de abrir -lo. Ele open()não retornará até que haja algum processo fazendo outra abertura no modo de gravação no canal (nesse momento, o canal será instanciado e o shell lerá a entrada dele).
— Stéphane Chazelas

Excelente ponto, @ StéphaneChazelas. Deve ser por isso que, uma vez exec 3>fexecutado, o primeiro shell fornece um prompt. (Ponto
— secundário

1
sim, desculpe. Editado agora antes do prazo de 5 minutos
— Stéphane Chazelas

Respostas:


12

Tem a ver com o fechamento do descritor de arquivo.

No seu primeiro exemplo, echograva em seu fluxo de saída padrão com o qual o shell é aberto para conectá-lo e f, quando termina, seu descritor é fechado (pelo shell). No lado de recebimento, o shell, que lê a entrada de seu fluxo de entrada padrão (conectado a f) lê ls, é executado lse finalizado devido à condição de fim de arquivo em sua entrada padrão.

A condição de fim de arquivo ocorre porque todos os gravadores no pipe nomeado (apenas um neste exemplo) fecharam sua extremidade do pipe.

No seu segundo exemplo, exec 3>fabre o descritor de arquivo 3 para gravar fe depois echograva lsnele. É o shell que agora tem o descritor de arquivo aberto, não o echocomando. O descritor permanece aberto até você exec 3>&-. No lado de recebimento, o shell, que lê a entrada de seu fluxo de entrada padrão (conectado a f) lê ls, executa lse aguarda por mais entrada (já que o fluxo ainda está aberto).

O fluxo permanece aberto porque todos os gravadores (o shell, via exec 3>fe echo) não fecharam a extremidade do tubo ( exec 3>fainda está em vigor).


Eu escrevi sobre echoacima como se fosse um comando externo. Provavelmente está embutido no shell. O efeito é o mesmo, no entanto.


6

Não há muito o que fazer: quando não há gravadores no canal, ele parece fechado para os leitores, ou seja, retorna EOF quando lido e bloqueia quando aberto.

Na página do manual do Linux ( pipe(7), mas veja também fifo(7)):

Se todos os descritores de arquivo referentes à extremidade de gravação de um canal tiverem sido fechados, uma tentativa read(2)do canal verá o final do arquivo ( read(2)retornará 0).

Fechar o final da gravação é o que acontece implicitamente no final do echo ls >fe, como você diz, no outro caso, o descritor de arquivo é mantido aberto.


Parece meio análogo às contagens de referência em Java (e outras linguagens OO)! Faz sentido embora.
— Fixee

2

Depois de ler as duas respostas de @Kusalananda e @ikkachu, acho que entendi. Na janela 1, o shell está aguardando algo abrir a extremidade de gravação do pipe e depois fechá-la. Depois que o final da gravação é aberto, o shell na janela-1 imprime um prompt. Depois que o final da gravação é fechado, o shell obtém EOF e morre.

Do lado da janela-2 temos as duas situações descritas na minha pergunta: na primeira situação com echo ls > f, não há nenhum descritor de arquivo 3, por isso temos echode desova, e sua stdine stdoutolhar como este:

0 --> tty
1 --> f

Em seguida, echotermina e o shell fecha os dois descritores. Como o descritor de arquivo 1 está fechado e faz referência f, o final da gravação de fé fechado e isso faz com que um EOF seja exibido na janela 1.

Na segunda situação, executamos exec 3>fem nosso shell, fazendo com que o shell pegue este ambiente:

bash:
0 --> tty
1 --> tty
2 --> tty
3 --> f

Agora, rodamos echo ls >& 3e o shell aloca descritores de arquivos da echoseguinte maneira:

echo:
0 --> tty
1 --> f     # because 3 points to f
2 --> tty

Em seguida, o shell fecha os três descritores acima, incluindo f, mas fainda tem uma referência a partir do próprio shell. Essa é a diferença importante. Fechar o descritor 3 exec 3>&-fecharia a última referência aberta e causaria um EOF na janela 1, como observou @Kusalananda.


Este é um bom exemplo de por que você deve deixar os três primeiros descritores de arquivo em paz, a menos que haja um bom motivo de design para modificá-los. Quando você usou o descritor (1) que acabou sendo o descritor de entrada (0) para o outro shell, você não apenas fechou o canal (e o que estava fazendo com esse fluxo de dados específico), mas também fechou a entrada no segundo shell que causou o seu término. Isso é bom, mas apenas se você estiver fazendo isso de propósito. O uso de descritores de arquivos com números mais altos evita efeitos colaterais como esse, porque nada espera que eles estejam em um estado específico ou mesmo definido.
— 23418 Joe

Para ser sincero, não sei ao certo o que estava tentando dizer nesse comentário, vou excluí-lo.
— Stéphane Chazelas
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.