Compreendendo comandos canalizados no Unix / Linux


16

Eu tenho dois programas simples: Ae B. Afuncionaria primeiro, depois Bpegaria o "stdout" de Ae o usaria como seu "stdin". Suponha que eu estou usando um sistema operacional GNU / Linux e a maneira mais simples possível de fazer isso seria:

./A | ./B

Se eu tivesse que descrever esse comando, diria que é um comando que recebe a entrada (isto é, lê) de um produtor ( A) e grava em um consumidor ( B). Essa é uma descrição correta? Estou faltando alguma coisa?



Não é comando, é um objeto kenerl criado pelo processo bash, usado como stdout do processo A e stdin como B. Dois processos são iniciados quase ao mesmo tempo.
precisa saber é o seguinte

11
@炸鱼você está correto - para o encanamento do kernel é um objeto em pipefs sistema de arquivos, mas, tanto quanto gastar em si está em causa - tecnicamente isso é um comando gasoduto
Sergiy Kolodyazhnyy

Respostas:


26

A única coisa sobre sua pergunta que se destaca como errada é que você diz

A seria executado primeiro, então B recebe o stdout de A

De fato, ambos os programas seriam iniciados praticamente ao mesmo tempo. Se não houver entrada para Bquando tentar ler, ele será bloqueado até que haja entrada para leitura. Da mesma forma, se não houver ninguém lendo a saída A, suas gravações serão bloqueadas até que a saída seja lida (algumas delas serão armazenadas em buffer pelo canal).

A única coisa que sincroniza os processos que participam de um pipeline é a E / S, ou seja, a leitura e gravação no canal. Se nenhuma gravação ou leitura acontecer, os dois processos serão executados totalmente independentes um do outro. Se um ignorar a leitura ou gravação do outro, o processo ignorado bloqueará e, eventualmente, será morto por um SIGPIPEsinal (se estiver gravando) ou obterá uma condição de fim de arquivo em seu fluxo de entrada padrão (se estiver lendo) quando o outro processo terminar .

A maneira idiomática de descrever A | Bé que é um pipeline contendo dois programas. A saída produzida na saída padrão do primeiro programa está disponível para ser lida na entrada padrão pelo segundo ("[a saída de] Aé canalizada para [a entrada de] B"). A concha faz o encanamento necessário para permitir que isso aconteça.

Se você quiser usar as palavras "consumidor" e "produtor", suponho que tudo bem também.

O fato de serem programas escritos em C não é relevante. O fato de ser Linux, macOS, OpenBSD ou AIX não é relevante.


2
A gravação em um arquivo temporário foi usada no DOS, pois não suportava vários processos.
CSM

2
@AlexVong Observe que seu exemplo com um arquivo temporário não é exatamente equivalente. Um programa pode optar por buscar o conteúdo de um arquivo, mas os dados provenientes de um canal não são procuráveis. Um examlp melhor seria usar mkfifopara criar um pipe nomeado, iniciar B na leitura de segundo plano do pipe e, em seguida, escrever A. Isso é muito difícil, pois o efeito seria o mesmo.
Kusalananda

2
@AlexVong As simplificações feitas nesse artigo o separam dos pipelines reais; a execução paralela é verdadeiramente semântica, não uma otimização. É uma explicação razoável de mentiras para crianças sobre avaliação ou composição monádica para alguém que já viu pipelines de shell, mas não é válido na outra direção. A versão quino de Kusalananda está mais próxima, mas as partes de propagação de erros do modelo são genuinamente importantes e não replicáveis. (todos os que eu digo como alguém que é muito em "shell pipelines são composição de função apenas" trem)
Michael Homer

6
@AlexVong Não, isso é completamente errado . Isso não é capaz de explicar até mesmo algo simples como yes | sed 10q
Tio Billy

11
@UncleBilly Concordo com o seu exemplo. Isso mostra que a execução paralela é realmente necessária também observada por Michael. Caso contrário, obteremos não rescisão.
Alex Vong

2

O termo geralmente usado na documentação é "pipeline", que consiste em um ou mais comandos, consulte definição de POSIX. Tecnicamente, são dois comandos que você tem lá, dois subprocessos para o shell ( fork()+exec()comandos externos ou subshells)

Quanto à parte produtor-consumidor , o pipeline pode ser descrito por esse padrão, pois:

  • Produtor e Consumidor compartilham buffer de tamanho fixo, e pelo menos no Linux e MacOS X, há tamanho fixo para buffer de pipeline
  • Produtor e Consumidor são fracamente acoplados, os comandos no pipeline não sabem a existência um do outro (a menos que estejam verificando ativamente o /proc/<pid>/fddiretório).
  • Os produtores escrevem stdoute os consumidores leem stdincomo se fossem um único comando sendo executado, ou seja, eles podem existir um sem o outro .

A diferença que vejo aqui é que, diferentemente do Producer-Consumer em outros idiomas, os comandos do shell usam buffer e escrevem stdout quando o buffer é preenchido, mas não há menção de que o Producer-Consumer deva seguir essa regra - aguarde apenas quando a fila for preenchida ou descartada dados (que é outra coisa que o pipeline não faz).

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.